1
0
mirror of https://github.com/gnosygnu/xowa.git synced 2026-03-02 03:49:30 +00:00

Xomw: Move Mw_parse classes into separate project

This commit is contained in:
gnosygnu
2017-02-08 17:38:39 -05:00
parent fdf6c49a05
commit 9a19be675e
117 changed files with 394 additions and 260 deletions

View File

@@ -0,0 +1,304 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.mediawiki.includes.media; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import gplx.xowa.mediawiki.includes.filerepo.file.*; import gplx.xowa.mediawiki.includes.parsers.lnkis.*;
import gplx.xowa.mediawiki.includes.utls.*;
/* XO.TODO:
* validateThumbParams
*/
// MEMORY:only one instance per wiki
public abstract class Xomw_ImageHandler extends Xomw_MediaHandler { private final Xomw_param_map paramMap = new Xomw_param_map();
public Xomw_ImageHandler(byte[] key) {super(key);
paramMap.Add(Xomw_param_itm.Mw__img_width, Xomw_param_map.Type__handler, Xomw_param_itm.Name_bry__width);
}
/**
* @param File file
* @return boolean
*/
@Override public boolean canRender(Xomw_File file) {
return (Php_utl_.istrue(file.getWidth()) && Php_utl_.istrue(file.getHeight()));
}
@Override public Xomw_param_map getParamMap() {
// XO.MW: defined above: "return [ 'img_width' => 'width' ];"
return paramMap;
}
@Override public boolean validateParam(int name_uid, byte[] val_bry, int val_int) {
if (name_uid == Xomw_param_itm.Name__width || name_uid == Xomw_param_itm.Name__height) {
if (val_int <= 0) {
return false;
}
else {
return true;
}
}
else {
return false;
}
}
@Override public byte[] makeParamString(Xomw_params_handler handlerParams) {
int width = 0;
if (Php_utl_.isset(handlerParams.physicalWidth)) {
width = handlerParams.physicalWidth;
}
else if (Php_utl_.isset(handlerParams.width)) {
width = handlerParams.width;
}
else {
throw Err_.new_wo_type("No width specified to makeParamString");
}
// Removed for ProofreadPage
// width = intval(width);
return Bry_.Add(Int_.To_bry(width), Xomw_lnki_wkr.Bry__px);
}
// public Xomw_param_map parseParamString(byte[] src) {
// int len = src.length;
// // XO.MW.REGEX: if (preg_match('/^(\d+)px/', str, m)) {
// if ( len > 0 // at least one char
// && Byte_ascii.Is_num(src[0])) // 1st char is numeric
// {
// pos = Bry_find_.Find_fwd_while_num(src, 1, len); // skip numeric
// if (Bry_.Match(src, pos, len, Xomw_lnki_wkr.Bry__px)) { // matches "px"
// Xomw_params_handler rv = new Xomw_params_handler();
// rv.width = Bry_.To_int_or(src, 0, pos, Php_utl_.Null_int);
// return rv;
// }
// }
// return null;
// }
// function getScriptParams(paramsVar) {
// return [ 'width' => paramsVar['width'] ];
// }
/**
* @param File image
* @param array paramsVar
* @return boolean
*/
@Override public boolean normaliseParams(Xomw_File image, Xomw_params_handler handlerParams) {
byte[] mimeType = image.getMimeType();
if (!Php_utl_.isset(handlerParams.width)) {
return false;
}
if (!Php_utl_.isset(handlerParams.page)) {
handlerParams.page = 1;
}
else {
// handlerParams.page = intval(handlerParams.page);
// if (handlerParams.page > image.pageCount()) {
// handlerParams.page = image.pageCount();
// }
//
// if (handlerParams.page < 1) {
// handlerParams.page = 1;
// }
}
int srcWidth = image.getWidth(handlerParams.page);
int srcHeight = image.getHeight(handlerParams.page);
if (Php_utl_.isset(handlerParams.height) && handlerParams.height != -1) {
// Height & width were both set
if (handlerParams.width * srcHeight > handlerParams.height * srcWidth) {
// Height is the relative smaller dimension, so scale width accordingly
handlerParams.width = fitBoxWidth(srcWidth, srcHeight, handlerParams.height);
if (handlerParams.width == 0) {
// Very small image, so we need to rely on client side scaling :(
handlerParams.width = 1;
}
handlerParams.physicalWidth = handlerParams.width;
} else {
// Height was crap, unset it so that it will be calculated later
handlerParams.height = Php_utl_.Null_int;
}
}
if (!Php_utl_.isset(handlerParams.physicalWidth)) {
// Passed all validations, so set the physicalWidth
handlerParams.physicalWidth = handlerParams.width;
}
// Because thumbs are only referred to by width, the height always needs
// to be scaled by the width to keep the thumbnail sizes consistent,
// even if it was set inside the if block above
handlerParams.physicalHeight = Xomw_File.scaleHeight(srcWidth, srcHeight,
handlerParams.physicalWidth);
// Set the height if it was not validated in the if block higher up
if (!Php_utl_.isset(handlerParams.height) || handlerParams.height == -1) {
handlerParams.height = handlerParams.physicalHeight;
}
if (!this.validateThumbParams(handlerParams, srcWidth, srcHeight, mimeType)
) {
return false;
}
return true;
}
/**
* Validate thumbnail parameters and fill in the correct height
*
* @param int width Specified width (input/output)
* @param int height Height (output only)
* @param int srcWidth Width of the source image
* @param int srcHeight Height of the source image
* @param String mimeType Unused
* @return boolean False to indicate that an error should be returned to the user.
*/
// XO.MW.NOTE: MW passes w and h by ref, but only changes h; XO will pass handlerParams directly
private boolean validateThumbParams(Xomw_params_handler handlerParams, int srcWidth, int srcHeight, byte[] mimeType) {
int width = handlerParams.physicalWidth;
int height = handlerParams.physicalHeight;
// width = intval(width);
// Sanity check width
if (width <= 0) {
Gfo_usr_dlg_.Instance.Warn_many("", "", "validateThumbParams: Invalid destination width: width");
return false;
}
if (srcWidth <= 0) {
Gfo_usr_dlg_.Instance.Warn_many("", "", "validateThumbParams: Invalid source width: srcWidth");
return false;
}
height = Xomw_File.scaleHeight(srcWidth, srcHeight, width);
if (height == 0) {
// Force height to be at least 1 pixel
height = 1;
}
handlerParams.height = height;
return true;
}
// /**
// * @param File image
// * @param String script
// * @param array paramsVar
// * @return boolean|MediaTransformOutput
// */
// function getScriptedTransform(image, script, paramsVar) {
// if (!this.normaliseParams(image, paramsVar)) {
// return false;
// }
// url = wfAppendQuery(script, this.getScriptParams(paramsVar));
//
// if (image.mustRender() || paramsVar['width'] < image.getWidth()) {
// return new ThumbnailImage(image, url, false, paramsVar);
// }
// }
//
// function getImageSize(image, path) {
// MediaWiki\suppressWarnings();
// gis = getimagesize(path);
// MediaWiki\restoreWarnings();
//
// return gis;
// }
//
// /**
// * Function that returns the number of pixels to be thumbnailed.
// * Intended for animated GIFs to multiply by the number of frames.
// *
// * If the file doesn't support a notion of "area" return 0.
// *
// * @param File image
// * @return int
// */
// function getImageArea(image) {
// return image.getWidth() * image.getHeight();
// }
//
// /**
// * @param File file
// * @return String
// */
// function getShortDesc(file) {
// global wgLang;
// nbytes = htmlspecialchars(wgLang.formatSize(file.getSize()));
// widthheight = wfMessage('widthheight')
// .numParams(file.getWidth(), file.getHeight()).escaped();
//
// return "widthheight (nbytes)";
// }
//
// /**
// * @param File file
// * @return String
// */
// function getLongDesc(file) {
// global wgLang;
// pages = file.pageCount();
// size = htmlspecialchars(wgLang.formatSize(file.getSize()));
// if (pages === false || pages <= 1) {
// msg = wfMessage('file-info-size').numParams(file.getWidth(),
// file.getHeight()).paramsVar(size,
// '<span class="mime-type">' . file.getMimeType() . '</span>').parse();
// } else {
// msg = wfMessage('file-info-size-pages').numParams(file.getWidth(),
// file.getHeight()).paramsVar(size,
// '<span class="mime-type">' . file.getMimeType() . '</span>').numParams(pages).parse();
// }
//
// return msg;
// }
//
// /**
// * @param File file
// * @return String
// */
// function getDimensionsString(file) {
// pages = file.pageCount();
// if (pages > 1) {
// return wfMessage('widthheightpage')
// .numParams(file.getWidth(), file.getHeight(), pages).text();
// } else {
// return wfMessage('widthheight')
// .numParams(file.getWidth(), file.getHeight()).text();
// }
// }
//
// public function sanitizeParamsForBucketing(paramsVar) {
// paramsVar = parent::sanitizeParamsForBucketing(paramsVar);
//
// // We unset the height parameters in order to let normaliseParams recalculate them
// // Otherwise there might be a height discrepancy
// if (isset(paramsVar['height'])) {
// unset(paramsVar['height']);
// }
//
// if (isset(paramsVar['physicalHeight'])) {
// unset(paramsVar['physicalHeight']);
// }
//
// return paramsVar;
// }
}

View File

@@ -0,0 +1,63 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.mediawiki.includes.media; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import org.junit.*; import gplx.core.tests.*;
import gplx.xowa.mediawiki.includes.utls.*;
import gplx.xowa.mediawiki.includes.parsers.*; import gplx.xowa.mediawiki.includes.parsers.lnkis.*;
import gplx.xowa.mediawiki.includes.filerepo.*; import gplx.xowa.mediawiki.includes.filerepo.file.*;
public class Xomw_ImageHandler__tst {
private final Xomw_ImageHandler__fxt fxt = new Xomw_ImageHandler__fxt();
@Before public void init() {
fxt.Init__file("A.png", 400, 200);
}
@Test public void normaliseParams() {
// widthOnly; "Because thumbs are only referred to by width, the height always needs"
fxt.Test__normaliseParams(fxt.Make__handlerParams(200), fxt.Make__handlerParams(200, 100, 200, 100));
}
}
class Xomw_ImageHandler__fxt {
private final Xomw_ImageHandler handler;
private final Xomw_FileRepo repo = new Xomw_FileRepo(Bry_.new_a7("/orig"), Bry_.new_a7("/thumb"));
private final Xomw_parser_env env = new Xomw_parser_env();
private Xomw_File file;
public Xomw_ImageHandler__fxt() {
handler = new Xomw_TransformationalImageHandler(Bry_.new_a7("test_handler"));
}
public Xomw_params_handler Make__handlerParams(int w) {return Make__handlerParams(w, Php_utl_.Null_int, Php_utl_.Null_int, Php_utl_.Null_int);}
public Xomw_params_handler Make__handlerParams(int w, int h, int phys_w, int phys_h) {
Xomw_params_handler rv = new Xomw_params_handler();
rv.width = w;
rv.height = h;
rv.physicalWidth = phys_w;
rv.physicalHeight = phys_h;
return rv;
}
public void Init__file(String title, int w, int h) {
this.file = new Xomw_LocalFile(env, Bry_.new_u8(title), repo, w, h, Xomw_MediaHandlerFactory.Mime__image__png);
}
public void Test__normaliseParams(Xomw_params_handler prms, Xomw_params_handler expd) {
// exec
handler.normaliseParams(file, prms);
// test
Gftest.Eq__int(expd.width, prms.width);
Gftest.Eq__int(expd.height, prms.height);
Gftest.Eq__int(expd.physicalWidth, prms.physicalWidth);
Gftest.Eq__int(expd.physicalHeight, prms.physicalHeight);
}
}

View File

@@ -0,0 +1,868 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.mediawiki.includes.media; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import gplx.xowa.mediawiki.includes.filerepo.file.*;
import gplx.xowa.mediawiki.includes.parsers.lnkis.*;
public abstract class Xomw_MediaHandler {
public byte[] Key() {return key;} private byte[] key;
public Xomw_MediaHandler(byte[] key) {
this.key = key;
}
private static final int TRANSFORM_LATER = 1;
// static final METADATA_GOOD = true;
// static final METADATA_BAD = false;
// static final METADATA_COMPATIBLE = 2; // for old but backwards compatible.
// /**
// * Max length of error logged by logErrorForExternalProcess()
// */
// static final MAX_ERR_LOG_SIZE = 65535;
//
// /**
// * Get a MediaHandler for a given MIME type from the instance cache
// *
// * @param String $type
// * @return MediaHandler|boolean
// */
// static function getHandler($type) {
// return MediaWikiServices::getInstance()
// ->getMediaHandlerFactory()->getHandler($type);
// }
/**
* Get an associative array mapping magic word IDs to parameter names.
* Will be used by the parser to identify parameters.
*/
public abstract Xomw_param_map getParamMap();
/**
* 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.
*
* @param String $name
* @param mixed $value
*/
public abstract boolean validateParam(int name_uid, byte[] val_bry, int val_int);
/**
* Merge a parameter array into a String appropriate for inclusion in filenames
*
* @param array paramsVar Array of parameters that have been through normaliseParams.
* @return String
*/
public abstract byte[] makeParamString(Xomw_params_handler handlerParams);
// /**
// * Parse a param String made with makeParamString back into an array
// *
// * @param String $str The parameter String without file name (e.g. 122px)
// * @return array|boolean Array of parameters or false on failure.
// */
// abstract public 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
* @param File $image
* @param array $paramsVar
*/
public abstract boolean normaliseParams(Xomw_File image, Xomw_params_handler handlerParams);
// /**
// * Get an image size array like that returned by getimagesize(), or false if it
// * can't be determined.
// *
// * This function is used for determining the width, height and bitdepth directly
// * from an image. The results are stored in the database in the img_width,
// * img_height, img_bits fields.
// *
// * @note If this is a multipage file, return the width and height of the
// * first page.
// *
// * @param File|FSFile $image The image Object, or false if there isn't one.
// * Warning, FSFile::getPropsFromPath might pass an FSFile instead of File (!)
// * @param String $path The filename
// * @return array|boolean Follow the format of PHP getimagesize() @gplx.Internal protected function.
// * See https://secure.php.net/getimagesize. MediaWiki will only ever use the
// * first two array keys (the width and height), and the 'bits' associative
// * key. All other array keys are ignored. Returning a 'bits' key is optional
// * as not all formats have a notion of "bitdepth". Returns false on failure.
// */
// abstract function getImageSize($image, $path);
//
// /**
// * Get handler-specific metadata which will be saved in the img_metadata field.
// *
// * @param File|FSFile $image The image Object, or false if there isn't one.
// * Warning, FSFile::getPropsFromPath might pass an FSFile instead of File (!)
// * @param String $path The filename
// * @return String A String of metadata in php serialized form (Run through serialize())
// */
// function getMetadata($image, $path) {
// return '';
// }
//
// /**
// * Get metadata version.
// *
// * This is not used for validating metadata, this is used for the api when returning
// * metadata, since api content formats should stay the same over time, and so things
// * using ForeignApiRepo can keep backwards compatibility
// *
// * All core media handlers share a common version number, and extensions can
// * use the GetMetadataVersion hook to append to the array (they should append a unique
// * String so not to get confusing). If there was a media handler named 'foo' with metadata
// * version 3 it might add to the end of the array the element 'foo=3'. if the core metadata
// * version is 2, the end version String would look like '2;foo=3'.
// *
// * @return String Version String
// */
// static function getMetadataVersion() {
// $version = [ '2' ]; // core metadata version
// Hooks::run('GetMetadataVersion', [ &$version ]);
//
// return implode(';', $version);
// }
//
// /**
// * Convert metadata version.
// *
// * By default just returns $metadata, but can be used to allow
// * media handlers to convert between metadata versions.
// *
// * @param String|array $metadata Metadata array (serialized if String)
// * @param int $version Target version
// * @return array Serialized metadata in specified version, or $metadata on fail.
// */
// function convertMetadataVersion($metadata, $version = 1) {
// if (!is_array($metadata)) {
//
// // unserialize to keep return parameter consistent.
// MediaWiki\suppressWarnings();
// $ret = unserialize($metadata);
// MediaWiki\restoreWarnings();
//
// return $ret;
// }
//
// return $metadata;
// }
//
// /**
// * Get a String describing the type of metadata, for display purposes.
// *
// * @note This method is currently unused.
// * @param File $image
// * @return String
// */
// function getMetadataType($image) {
// return false;
// }
//
// /**
// * Check if the metadata String is valid for this handler.
// * If it returns MediaHandler::METADATA_BAD (or false), Image
// * will reload the metadata from the file and update the database.
// * MediaHandler::METADATA_GOOD for if the metadata is a-ok,
// * MediaHandler::METADATA_COMPATIBLE if metadata is old but backwards
// * compatible (which may or may not trigger a metadata reload).
// *
// * @note Returning self::METADATA_BAD will trigger a metadata reload from
// * file on page view. Always returning this from a broken file, or suddenly
// * triggering as bad metadata for a large number of files can cause
// * performance problems.
// * @param File $image
// * @param String $metadata The metadata in serialized form
// * @return boolean
// */
// function isMetadataValid($image, $metadata) {
// return self::METADATA_GOOD;
// }
//
// /**
// * Get an array of standard (FormatMetadata type) metadata values.
// *
// * The returned data is largely the same as that from getMetadata(),
// * but formatted in a standard, stable, handler-independent way.
// * The idea being that some values like ImageDescription or Artist
// * are universal and should be retrievable in a handler generic way.
// *
// * The specific properties are the type of properties that can be
// * handled by the FormatMetadata class. These values are exposed to the
// * user via the filemetadata parser function.
// *
// * Details of the response format of this function can be found at
// * https://www.mediawiki.org/wiki/Manual:File_metadata_handling
// * tl/dr: the response is an associative array of
// * properties keyed by name, but the value can be complex. You probably
// * want to call one of the FormatMetadata::flatten* functions on the
// * property values before using them, or call
// * FormatMetadata::getFormattedData() on the full response array, which
// * transforms all values into prettified, human-readable text.
// *
// * Subclasses overriding this function must return a value which is a
// * valid API response fragment (all associative array keys are valid
// * XML tagnames).
// *
// * Note, if the file simply has no metadata, but the handler supports
// * this interface, it should return an empty array, not false.
// *
// * @param File $file
// * @return array|boolean False if interface not supported
// * @since 1.23
// */
// public function getCommonMetaArray(File $file) {
// return false;
// }
//
// /**
// * Get a MediaTransformOutput Object representing an alternate of the transformed
// * output which will call an intermediary thumbnail assist script.
// *
// * Used when the repository has a thumbnailScriptUrl option configured.
// *
// * Return false to fall back to the regular getTransform().
// * @param File $image
// * @param String $script
// * @param array $paramsVar
// * @return boolean|ThumbnailImage
// */
// function getScriptedTransform($image, $script, $paramsVar) {
// return false;
// }
/**
* Get a MediaTransformOutput Object representing the transformed output. Does not
* actually do the transform.
*
* @param File $image The image Object
* @param String $dstPath Filesystem destination path
* @param String $dstUrl Destination URL to use in output HTML
* @param array $paramsVar Arbitrary set of parameters validated by $this->validateParam()
* @return MediaTransformOutput
*/
public Xomw_MediaTransformOutput getTransform(Xomw_File image, byte[] dstPath, byte[] dstUrl, Xomw_params_handler handlerParams) {
return this.doTransform(image, dstPath, dstUrl, handlerParams, TRANSFORM_LATER);
}
/**
* Get a MediaTransformOutput Object representing the transformed output. Does the
* transform unless $flags contains self::TRANSFORM_LATER.
*
* @param File $image The image Object
* @param String $dstPath Filesystem destination path
* @param String $dstUrl Destination URL to use in output HTML
* @param array $paramsVar Arbitrary set of parameters validated by $this->validateParam()
* Note: These parameters have *not* gone through $this->normaliseParams()
* @param int $flags A bitfield, may contain self::TRANSFORM_LATER
* @return MediaTransformOutput
*/
public Xomw_MediaTransformOutput doTransform(Xomw_File image, byte[] dstPath, byte[] dstUrl, Xomw_params_handler handlerParams) {return doTransform(image, dstPath, dstUrl, handlerParams, 0);}
public abstract Xomw_MediaTransformOutput doTransform(Xomw_File image, byte[] dstPath, byte[] dstUrl, Xomw_params_handler handlerParams, int flags);
// /**
// * Get the thumbnail extension and MIME type for a given source MIME type
// *
// * @param String $ext Extension of original file
// * @param String $mime MIME type of original file
// * @param array $paramsVar Handler specific rendering parameters
// * @return array Thumbnail extension and MIME type
// */
// function getThumbType($ext, $mime, $paramsVar = null) {
// $magic = MimeMagic::singleton();
// if (!$ext || $magic->isMatchingExtension($ext, $mime) === false) {
// // The extension is not valid for this MIME type and we do
// // recognize the MIME type
// $extensions = $magic->getExtensionsForType($mime);
// if ($extensions) {
// return [ strtok($extensions, ' '), $mime ];
// }
// }
//
// // The extension is correct (true) or the MIME type is unknown to
// // MediaWiki (null)
// return [ $ext, $mime ];
// }
//
// /**
// * Get useful response headers for GET/HEAD requests for a file with the given metadata
// *
// * @param mixed $metadata Result of the getMetadata() function of this handler for a file
// * @return array
// */
// public function getStreamHeaders($metadata) {
// return [];
// }
/**
* True if the handled types can be transformed
*
* @param File $file
* @return boolean
*/
@gplx.Virtual public boolean canRender(Xomw_File file) {
return true;
}
/**
* True if handled types cannot be displayed directly in a browser
* but can be rendered
*
* @param File $file
* @return boolean
*/
public boolean mustRender(Xomw_File file) {
return false;
}
// /**
// * True if the type has multi-page capabilities
// *
// * @param File $file
// * @return boolean
// */
// public function isMultiPage($file) {
// return false;
// }
//
// /**
// * Page count for a multi-page document, false if unsupported or unknown
// *
// * @param File $file
// * @return boolean
// */
// function pageCount(File $file) {
// return false;
// }
//
// /**
// * The material is vectorized and thus scaling is lossless
// *
// * @param File $file
// * @return boolean
// */
// function isVectorized($file) {
// return false;
// }
//
// /**
// * The material is an image, and is animated.
// * In particular, video material need not return true.
// * @note Before 1.20, this was a method of ImageHandler only
// *
// * @param File $file
// * @return boolean
// */
// function isAnimatedImage($file) {
// return false;
// }
//
// /**
// * If the material is animated, we can animate the thumbnail
// * @since 1.20
// *
// * @param File $file
// * @return boolean If material is not animated, handler may return any value.
// */
// function canAnimateThumbnail($file) {
// return true;
// }
//
// /**
// * False if the handler is disabled for all files
// * @return boolean
// */
// 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.
// *
// * It is expected that handlers for paged media (e.g. DjVuHandler)
// * will override this method so that it gives the correct results
// * for each specific page of the file, using the $page argument.
// *
// * @note For non-paged media, use getImageSize.
// *
// * @param File $image
// * @param int $page What page to get dimensions of
// * @return array|boolean
// */
// function getPageDimensions(File $image, $page) {
// $gis = $this->getImageSize($image, $image->getLocalRefPath());
// if ($gis) {
// return [
// 'width' => $gis[0],
// 'height' => $gis[1]
// ];
// } else {
// return false;
// }
// }
//
// /**
// * Generic getter for text layer.
// * Currently overloaded by PDF and DjVu handlers
// * @param File $image
// * @param int $page Page number to get information for
// * @return boolean|String Page text or false when no text found or if
// * unsupported.
// */
// function getPageText(File $image, $page) {
// return false;
// }
//
// /**
// * Get the text of the entire document.
// * @param File $file
// * @return boolean|String The text of the document or false if unsupported.
// */
// public function getEntireText(File $file) {
// $numPages = $file->pageCount();
// if (!$numPages) {
// // Not a multipage document
// return $this->getPageText($file, 1);
// }
// $document = '';
// for ($i = 1; $i <= $numPages; $i++) {
// $curPage = $this->getPageText($file, $i);
// if (is_string($curPage)) {
// $document .= $curPage . "\n";
// }
// }
// if ($document !== '') {
// return $document;
// }
// return false;
// }
//
// /**
// * Get an array structure that looks like this:
// *
// * [
// * 'visible' => [
// * 'Human-readable name' => 'Human readable value',
// * ...
// * ],
// * 'collapsed' => [
// * 'Human-readable name' => 'Human readable value',
// * ...
// * ]
// * ]
// * The UI will format this into a table where the visible fields are always
// * visible, and the collapsed fields are optionally visible.
// *
// * The function should return false if there is no metadata to display.
// */
//
// /**
// * @todo FIXME: This interface is not very flexible. The media handler
// * should generate HTML instead. It can do all the formatting according
// * to some standard. That makes it possible to do things like visual
// * indication of grouped and chained streams in ogg container files.
// * @param File $image
// * @param boolean|IContextSource $context Context to use (optional)
// * @return array|boolean
// */
// function formatMetadata($image, $context = false) {
// return false;
// }
//
// /** sorts the visible/invisible field.
// * Split off from ImageHandler::formatMetadata, as used by more than
// * one type of handler.
// *
// * This is used by the media handlers that use the FormatMetadata class
// *
// * @param array $metadataArray Metadata array
// * @param boolean|IContextSource $context Context to use (optional)
// * @return array Array for use displaying metadata.
// */
// function formatMetadataHelper($metadataArray, $context = false) {
// $result = [
// 'visible' => [],
// 'collapsed' => []
// ];
//
// $formatted = FormatMetadata::getFormattedData($metadataArray, $context);
// // Sort fields into visible and collapsed
// $visibleFields = $this->visibleMetadataFields();
// foreach ($formatted as $name => $value) {
// $tag = strtolower($name);
// self::addMeta($result,
// in_array($tag, $visibleFields) ? 'visible' : 'collapsed',
// 'exif',
// $tag,
// $value
// );
// }
//
// return $result;
// }
//
// /**
// * Get a list of metadata items which should be displayed when
// * the metadata table is collapsed.
// *
// * @return array Array of strings
// */
// protected function visibleMetadataFields() {
// return FormatMetadata::getVisibleFields();
// }
//
// /**
// * This is used to generate an array element for each metadata value
// * That array is then used to generate the table of metadata values
// * on the image page
// *
// * @param array &$array An array containing elements for each type of visibility
// * and each of those elements being an array of metadata items. This function adds
// * a value to that array.
// * @param String $visibility ('visible' or 'collapsed') if this value is hidden
// * by default.
// * @param String $type Type of metadata tag (currently always 'exif')
// * @param String $id The name of the metadata tag (like 'artist' for example).
// * its name in the table displayed is the message "$type-$id" (Ex exif-artist).
// * @param String $value Thingy goes into a wikitext table; it used to be escaped but
// * that was incompatible with previous practise of customized display
// * with wikitext formatting via messages such as 'exif-model-value'.
// * So the escaping is taken back out, but generally this seems a confusing
// * interface.
// * @param boolean|String $param Value to pass to the message for the name of the field
// * as $1. Currently this parameter doesn't seem to ever be used.
// *
// * Note, everything here is passed through the parser later on (!)
// */
// protected static function addMeta(&$array, $visibility, $type, $id, $value, $param = false) {
// $msg = wfMessage("$type-$id", $param);
// if ($msg->exists()) {
// $name = $msg->text();
// } else {
// // This is for future compatibility when using instant commons.
// // So as to not display as ugly a name if a new metadata
// // property is defined that we don't know about
// // (not a major issue since such a property would be collapsed
// // by default).
// wfDebug(__METHOD__ . ' Unknown metadata name: ' . $id . "\n");
// $name = wfEscapeWikiText($id);
// }
// $array[$visibility][] = [
// 'id' => "$type-$id",
// 'name' => $name,
// 'value' => $value
// ];
// }
//
// /**
// * Short description. Shown on Special:Search results.
// *
// * @param File $file
// * @return String
// */
// function getShortDesc($file) {
// return self::getGeneralShortDesc($file);
// }
//
// /**
// * Long description. Shown under image on image description page surounded by ().
// *
// * @param File $file
// * @return String
// */
// function getLongDesc($file) {
// return self::getGeneralLongDesc($file);
// }
//
// /**
// * Used instead of getShortDesc if there is no handler registered for file.
// *
// * @param File $file
// * @return String
// */
// static function getGeneralShortDesc($file) {
// global $wgLang;
//
// return htmlspecialchars($wgLang->formatSize($file->getSize()));
// }
//
// /**
// * Used instead of getLongDesc if there is no handler registered for file.
// *
// * @param File $file
// * @return String
// */
// static function getGeneralLongDesc($file) {
// return wfMessage('file-info')->sizeParams($file->getSize())
// ->paramsVar('<span class="mime-type">' . $file->getMimeType() . '</span>')->parse();
// }
/**
* Calculate the largest thumbnail width for a given original file size
* such that the thumbnail's height is at most $maxHeight.
* @param int $boxWidth Width of the thumbnail box.
* @param int $boxHeight Height of the thumbnail box.
* @param int $maxHeight Maximum height expected for the thumbnail.
* @return int
*/
public static int fitBoxWidth(int boxWidth, int boxHeight, int maxHeight) {
double idealWidth = boxWidth * maxHeight / boxHeight;
int roundedUp = Math_.Ceil_as_int(idealWidth);
if (Math_.Round(roundedUp * boxHeight / boxWidth, 0) > maxHeight) {
return Math_.Floor_as_int(idealWidth);
} else {
return roundedUp;
}
}
// /**
// * Shown in file history box on image description page.
// *
// * @param File $file
// * @return String Dimensions
// */
// function getDimensionsString($file) {
// return '';
// }
//
// /**
// * Modify the parser Object post-transform.
// *
// * This is often used to do $parser->addOutputHook(),
// * in order to add some javascript to render a viewer.
// * See TimedMediaHandler or OggHandler for an example.
// *
// * @param Parser $parser
// * @param File $file
// */
// function parserTransformHook($parser, $file) {
// }
//
// /**
// * File validation hook called on upload.
// *
// * If the file at the given local path is not valid, or its MIME type does not
// * match the handler class, a Status Object should be returned containing
// * relevant errors.
// *
// * @param String $fileName The local path to the file.
// * @return Status
// */
// function verifyUpload($fileName) {
// return Status::newGood();
// }
//
// /**
// * Check for zero-sized thumbnails. These can be generated when
// * no disk space is available or some other error occurs
// *
// * @param String $dstPath The location of the suspect file
// * @param int $retval Return value of some shell process, file will be deleted if this is non-zero
// * @return boolean True if removed, false otherwise
// */
// function removeBadFile($dstPath, $retval = 0) {
// if (file_exists($dstPath)) {
// $thumbstat = stat($dstPath);
// if ($thumbstat['size'] == 0 || $retval != 0) {
// $result = unlink($dstPath);
//
// if ($result) {
// wfDebugLog('thumbnail',
// sprintf('Removing bad %d-byte thumbnail "%s". unlink() succeeded',
// $thumbstat['size'], $dstPath));
// } else {
// wfDebugLog('thumbnail',
// sprintf('Removing bad %d-byte thumbnail "%s". unlink() failed',
// $thumbstat['size'], $dstPath));
// }
//
// return true;
// }
// }
//
// return false;
// }
//
// /**
// * Remove files from the purge list.
// *
// * This is used by some video handlers to prevent ?action=purge
// * from removing a transcoded video, which is expensive to
// * regenerate.
// *
// * @see LocalFile::purgeThumbnails
// *
// * @param array $files
// * @param array $options Purge options. Currently will always be
// * an array with a single key 'forThumbRefresh' set to true.
// */
// public function filterThumbnailPurgeList(&$files, $options) {
// // Do nothing
// }
//
// /**
// * True if the handler can rotate the media
// * @since 1.24 non-static. From 1.21-1.23 was static
// * @return boolean
// */
// public function canRotate() {
// return false;
// }
//
// /**
// * On supporting image formats, try to read out the low-level orientation
// * of the file and return the angle that the file needs to be rotated to
// * be viewed.
// *
// * This information is only useful when manipulating the original file;
// * the width and height we normally work with is logical, and will match
// * any produced output views.
// *
// * For files we don't know, we return 0.
// *
// * @param File $file
// * @return int 0, 90, 180 or 270
// */
// public function getRotation($file) {
// return 0;
// }
//
// /**
// * Log an error that occurred in an external process
// *
// * Moved from BitmapHandler to MediaHandler with MediaWiki 1.23
// *
// * @since 1.23
// * @param int $retval
// * @param String $err Error reported by command. Anything longer than
// * MediaHandler::MAX_ERR_LOG_SIZE is stripped off.
// * @param String $cmd
// */
// protected function logErrorForExternalProcess($retval, $err, $cmd) {
// # Keep error output limited (bug 57985)
// $errMessage = trim(substr($err, 0, self::MAX_ERR_LOG_SIZE));
//
// wfDebugLog('thumbnail',
// sprintf('thumbnail failed on %s: error %d "%s" from "%s"',
// wfHostname(), $retval, $errMessage, $cmd));
// }
//
// /**
// * Get list of languages file can be viewed in.
// *
// * @param File $file
// * @return String[] Array of language codes, or empty array if unsupported.
// * @since 1.23
// */
// public function getAvailableLanguages(File $file) {
// return [];
// }
//
// /**
// * On file types that support renderings in multiple languages,
// * which language is used by default if unspecified.
// *
// * If getAvailableLanguages returns a non-empty array, this must return
// * a valid language code. Otherwise can return null if files of this
// * type do not support alternative language renderings.
// *
// * @param File $file
// * @return String|null Language code or null if multi-language not supported for filetype.
// * @since 1.23
// */
// public function getDefaultRenderLanguage(File $file) {
// return null;
// }
//
// /**
// * If its an audio file, return the length of the file. Otherwise 0.
// *
// * File::getLength() existed for a long time, but was calling a method
// * that only existed in some subclasses of this class (The TMH ones).
// *
// * @param File $file
// * @return float length in seconds
// * @since 1.23
// */
// public function getLength($file) {
// return 0.0;
// }
//
// /**
// * True if creating thumbnails from the file is large or otherwise resource-intensive.
// * @param File $file
// * @return boolean
// */
// public function isExpensiveToThumbnail($file) {
// return false;
// }
//
// /**
// * Returns whether or not this handler supports the chained generation of thumbnails according
// * to buckets
// * @return boolean
// * @since 1.24
// */
// public function supportsBucketing() {
// return false;
// }
//
// /**
// * Returns a normalised paramsVar array for which parameters have been cleaned up for bucketing
// * purposes
// * @param array $paramsVar
// * @return array
// */
// public function sanitizeParamsForBucketing($paramsVar) {
// return $paramsVar;
// }
//
// /**
// * Gets configuration for the file warning message. Return value of
// * the following structure:
// * [
// * // Required, module with messages loaded for the client
// * 'module' => 'example.filewarning.messages',
// * // Required, array of names of messages
// * 'messages' => [
// * // Required, main warning message
// * 'main' => 'example-filewarning-main',
// * // Optional, header for warning dialog
// * 'header' => 'example-filewarning-header',
// * // Optional, footer for warning dialog
// * 'footer' => 'example-filewarning-footer',
// * // Optional, text for more-information link (see below)
// * 'info' => 'example-filewarning-info',
// * ],
// * // Optional, link for more information
// * 'link' => 'http://example.com',
// * ]
// *
// * Returns null if no warning is necessary.
// * @param File $file
// * @return array|null
// */
// public function getWarningConfig($file) {
// return null;
// }
}

View File

@@ -0,0 +1,63 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.mediawiki.includes.media; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
// XO.MW:MW has registry and instance cache; XO only has instance
// XO.MW:SYNC:1.29; DATE:2017-02-05
public class Xomw_MediaHandlerFactory {
private final Hash_adp_bry handlers = Hash_adp_bry.cs();
// XO.MW:SYNC:1.29; DATE:2017-02-05
public Xomw_MediaHandlerFactory() {
// Default, MediaWiki core media handlers
// 'image/jpeg' => JpegHandler::class,
handlers.Add(Mime__image__png, new Xomw_TransformationalImageHandler(Mime__image__png)); // PngHandler
// 'image/gif' => GIFHandler::class,
// 'image/tiff' => TiffHandler::class,
// 'image/webp' => WebPHandler::class,
// 'image/x-ms-bmp' => BmpHandler::class,
// 'image/x-bmp' => BmpHandler::class,
// 'image/x-xcf' => XCFHandler::class,
// 'image/svg+xml' => SvgHandler::class, // official
// 'image/svg' => SvgHandler::class, // compat
// 'image/vnd.djvu' => DjVuHandler::class, // official
// 'image/x.djvu' => DjVuHandler::class, // compat
// 'image/x-djvu' => DjVuHandler::class, // compat
}
// XO.MW:SYNC:1.29; DATE:2017-02-05
public Xomw_MediaHandler getHandler(byte[] type) {
return (Xomw_MediaHandler)handlers.Get_by(type);
}
public static byte[]
Mime__image__jpeg = Bry_.new_a7("image/jpeg")
, Mime__image__png = Bry_.new_a7("image/png")
, Mime__image__gif = Bry_.new_a7("image/gif")
, Mime__image__tiff = Bry_.new_a7("image/tiff")
, Mime__image__webp = Bry_.new_a7("image/webp")
, Mime__image__x_ms_bmp = Bry_.new_a7("image/x-ms-bmp")
, Mime__image__x_bmp = Bry_.new_a7("image/x-bmp")
, Mime__image__x_xcf = Bry_.new_a7("image/x-xcf")
, Mime__image__svg_xml = Bry_.new_a7("image/svg+xml")
, Mime__image__svg = Bry_.new_a7("image/svg")
, Mime__image__vnd_djvu = Bry_.new_a7("image/vnd.djvu")
, Mime__image__x_djvu_dot = Bry_.new_a7("image/x.djvu")
, Mime__image__x_djvu_dash = Bry_.new_a7("image/x-djvu")
;
}

View File

@@ -0,0 +1,281 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.mediawiki.includes.media; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import gplx.langs.htmls.*;
import gplx.xowa.mediawiki.includes.utls.*;
import gplx.xowa.mediawiki.includes.parsers.lnkis.*;
import gplx.xowa.mediawiki.includes.filerepo.file.*;
public abstract class Xomw_MediaTransformOutput {
public Xomw_MediaTransformOutput(Xomw_File file, byte[] url, byte[] path, int width, int height) {
this.file = file;
this.url = url;
this.width = width;
this.height = height;
}
// /** @var array Associative array mapping optional supplementary image files
// * from pixel density (eg 1.5 or 2) to additional URLs.
// */
// public $responsiveUrls = [];
/** @var File */
private final Xomw_File file;
/** @var int Image width */
protected final int width;
/** @var int Image height */
protected final int height;
/** @var String URL path to the thumb */
protected final byte[] url;
// /** @var boolean|String */
// protected $page;
//
// /** @var boolean|String Filesystem path to the thumb */
// protected $path;
//
// /** @var boolean|String Language code, false if not set */
// protected $lang;
//
// /** @var boolean|String Permanent storage path */
// protected $storagePath = false;
/**
* @return int Width of the output box
*/
public int getWidth() {
return this.width;
}
/**
* @return int Height of the output box
*/
public int getHeight() {
return this.height;
}
// /**
// * @return File
// */
// public function getFile() {
// return $this->file;
// }
//
// /**
// * Get the final extension of the thumbnail.
// * Returns false for scripted transformations.
// * @return String|boolean
// */
// public function getExtension() {
// return $this->path ? FileBackend::extensionFromPath( $this->path ) : false;
// }
//
// /**
// * @return String|boolean The thumbnail URL
// */
// public function getUrl() {
// return $this->url;
// }
//
// /**
// * @return String|boolean The permanent thumbnail storage path
// */
// public function getStoragePath() {
// return $this->storagePath;
// }
//
// /**
// * @param String $storagePath The permanent storage path
// * @return void
// */
// public function setStoragePath( $storagePath ) {
// $this->storagePath = $storagePath;
// if ( $this->path === false ) {
// $this->path = $storagePath;
// }
// }
/**
* Fetch HTML for this transform output
*
* @param array $options Associative array of options. Boolean options
* should be indicated with a value of true for true, and false or
* absent for false.
*
* alt Alternate text or caption
* desc-link Boolean, show a description link
* file-link Boolean, show a file download link
* custom-url-link Custom URL to link to
* custom-title-link Custom Title Object to link to
* valign vertical-align property, if the output is an inline element
* img-class Class applied to the "<img>" tag, if there is such a tag
*
* For images, desc-link and file-link are implemented as a click-through. For
* sounds and videos, they may be displayed in other ways.
*
* @return String
*/
public abstract void toHtml(Bry_bfr bfr, Bry_bfr tmp, Xomw_params_mto options);
// /**
// * This will be overridden to return true in error classes
// * @return boolean
// */
// public function isError() {
// return false;
// }
//
// /**
// * Check if an output thumbnail file actually exists.
// *
// * This will return false if there was an error, the
// * thumbnail is to be handled client-side only, or if
// * transformation was deferred via TRANSFORM_LATER.
// * This file may exist as a new file in /tmp, a file
// * in permanent storage, or even refer to the original.
// *
// * @return boolean
// */
// public function hasFile() {
// // If TRANSFORM_LATER, $this->path will be false.
// // Note: a null path means "use the source file".
// return ( !$this->isError() && ( $this->path || $this->path === null ) );
// }
//
// /**
// * Check if the output thumbnail is the same as the source.
// * This can occur if the requested width was bigger than the source.
// *
// * @return boolean
// */
// public function fileIsSource() {
// return ( !$this->isError() && $this->path === null );
// }
//
// /**
// * Get the path of a file system copy of the thumbnail.
// * Callers should never write to this path.
// *
// * @return String|boolean Returns false if there isn't one
// */
// public function getLocalCopyPath() {
// if ( $this->isError() ) {
// return false;
// } elseif ( $this->path === null ) {
// return $this->file->getLocalRefPath(); // assume thumb was not scaled
// } elseif ( FileBackend::isStoragePath( $this->path ) ) {
// $be = $this->file->getRepo()->getBackend();
// // The temp file will be process cached by FileBackend
// $fsFile = $be->getLocalReference( [ 'src' => $this->path ] );
//
// return $fsFile ? $fsFile->getPath() : false;
// } else {
// return $this->path; // may return false
// }
// }
//
// /**
// * Stream the file if there were no errors
// *
// * @param array $headers Additional HTTP headers to send on success
// * @return Status
// * @since 1.27
// */
// public function streamFileWithStatus( $headers = [] ) {
// if ( !$this->path ) {
// return Status::newFatal( 'backend-fail-stream', '<no path>' );
// } elseif ( FileBackend::isStoragePath( $this->path ) ) {
// $be = $this->file->getRepo()->getBackend();
// return $be->streamFile( [ 'src' => $this->path, 'headers' => $headers ] );
// } else { // FS-file
// $success = StreamFile::stream( $this->getLocalCopyPath(), $headers );
// return $success ? Status::newGood() : Status::newFatal( 'backend-fail-stream', $this->path );
// }
// }
//
// /**
// * Stream the file if there were no errors
// *
// * @deprecated since 1.26, use streamFileWithStatus
// * @param array $headers Additional HTTP headers to send on success
// * @return boolean Success
// */
// public function streamFile( $headers = [] ) {
// $this->streamFileWithStatus( $headers )->isOK();
// }
//
// /**
// * Wrap some XHTML text in an anchor tag with the given attributes
// *
// * @param array $linkAttribs
// * @param String $contents
// * @return String
// */
// protected function linkWrap( $linkAttribs, $contents ) {
// if ( $linkAttribs ) {
// return Xml::tags( 'a', $linkAttribs, $contents );
// } else {
// return $contents;
// }
// }
/**
* @param String $title
* @param String|array $prms Query parameters to add
* @return array
*/
public void getDescLinkAttribs(List_adp attribs, byte[] title, List_adp prms) {
// if ( is_array( prms ) ) {
// $query = prms;
// } else {
// $query = [];
// }
// if ( $this->page && $this->page !== 1 ) {
// $query['page'] = $this->page;
// }
// if ( $this->lang ) {
// $query['lang'] = $this->lang;
// }
//
// if ( is_string( prms ) && prms !== '' ) {
// $query = prms . '&' . wfArrayToCgi( $query );
// }
attribs.Clear();
// 'href' => $this->file->getTitle()->getLocalURL( $query ),
attribs.Add_many(Gfh_atr_.Bry__href, this.file.getTitle());
attribs.Add_many(Gfh_atr_.Bry__class, Bry__class__image);
if (title != null) {
attribs.Add_many(Gfh_atr_.Bry__title, title);
}
}
// Wrap some XHTML text in an anchor tag with the given attributes
// XO.MW:SYNC:1.29; DATE:2017-02-03
protected void Link_wrap(Bry_bfr bfr, List_adp link_attribs, byte[] contents) {
if (link_attribs != null) {
Xomw_xml.Tags(bfr, Gfh_tag_.Bry__a, link_attribs, contents);
}
else {
bfr.Add(contents);
}
}
private static final byte[] Bry__class__image = Bry_.new_a7("image");
}

View File

@@ -0,0 +1,214 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.mediawiki.includes.media; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import gplx.langs.htmls.*;
import gplx.xowa.mediawiki.includes.utls.*;
import gplx.xowa.mediawiki.includes.parsers.lnkis.*;
import gplx.xowa.mediawiki.includes.filerepo.file.*;
// Media transform output for images
public class Xomw_ThumbnailImage extends Xomw_MediaTransformOutput { private final List_adp attribs = List_adp_.New(), link_attribs = List_adp_.New();
public Xomw_ThumbnailImage(Xomw_File file, byte[] url, byte[] path, int w, int h) {super(file, url, path, w, h);
}
/**
* Get a thumbnail Object from a file and parameters.
* If path is set to null, the output file is treated as a source copy.
* If path is set to false, no output file will be created.
* parameters should include, as a minimum, (file) 'width' and 'height'.
* It may also include a 'page' parameter for multipage files.
*
* @param File file
* @param String url URL path to the thumb
* @param String|boolean path Filesystem path to the thumb
* @param array parameters Associative array of parameters
*/
public Xomw_ThumbnailImage(Xomw_File file, byte[] url, byte[] path, Xomw_params_handler parameters) {super(file, url, path, parameters.width, parameters.height);
// defaults = [
// 'page' => false,
// 'lang' => false
// ];
//
// if (is_array(parameters)) {
// actualParams = parameters + defaults;
// } else {
// // Using old format, should convert. Later a warning could be added here.
// numArgs = func_num_args();
// actualParams = [
// 'width' => path,
// 'height' => parameters,
// 'page' => (numArgs > 5) ? func_get_arg(5) : false
// ] + defaults;
// path = (numArgs > 4) ? func_get_arg(4) : false;
// }
// this->file = file;
// this->url = url;
// this->path = path;
// 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(actualParams['width']);
// this->height = round(actualParams['height']);
// this->page = actualParams['page'];
// this->lang = actualParams['lang'];
}
/**
* Return HTML <img ... /> tag for the thumbnail, will include
* width and height attributes and a blank alt text (as required).
*
* @param array options Associative array of options. Boolean options
* should be indicated with a value of true for true, and false or
* absent for false.
*
* alt HTML alt attribute
* title HTML title attribute
* desc-link Boolean, show a description link
* file-link Boolean, show a file download link
* valign vertical-align property, if the output is an inline element
* img-class Class applied to the \<img\> tag, if there is such a tag
* desc-query String, description link query prms
* @Override width Override width attribute. Should generally not set
* @Override height Override height attribute. Should generally not set
* no-dimensions Boolean, skip width and height attributes (useful if
* set in CSS)
* custom-url-link Custom URL to link to
* custom-title-link Custom Title Object to link to
* custom target-link Value of the target attribute, for custom-target-link
* parser-extlink-* Attributes added by parser for external links:
* parser-extlink-rel: add rel="nofollow"
* parser-extlink-target: link target, but overridden by custom-target-link
*
* For images, desc-link and file-link are implemented as a click-through. For
* sounds and videos, they may be displayed in other ways.
*
* @throws MWException
* @return String
*/
// Return HTML <img ... /> tag for the thumbnail, will include
// width and height attributes and a blank alt text (as required).
//
// @param array options Associative array of options. Boolean options
// should be indicated with a value of true for true, and false or
// absent for false.
//
// alt HTML alt attribute
// title HTML title attribute
// desc-link Boolean, show a description link
// file-link Boolean, show a file download link
// valign vertical-align property, if the output is an inline element
// img-class Class applied to the \<img\> tag, if there is such a tag
// desc-query String, description link query prms
// override-width Override width attribute. Should generally not set
// override-height Override height attribute. Should generally not set
// no-dimensions Boolean, skip width and height attributes (useful if
// set in CSS)
// custom-url-link Custom URL to link to
// custom-title-link Custom Title Object to link to
// custom target-link Value of the target attribute, for custom-target-link
// parser-extlink-* Attributes added by parser for external links:
// parser-extlink-rel: add rel="nofollow"
// parser-extlink-target: link target, but overridden by custom-target-link
//
// For images, desc-link and file-link are implemented as a click-through. For
// sounds and videos, they may be displayed in other ways.
// XO.MW:SYNC:1.29; DATE:2017-02-03
@Override public void toHtml(Bry_bfr bfr, Bry_bfr tmp, Xomw_params_mto options) {
byte[] alt = options.alt;
// byte[] query = options.desc_query;
attribs.Clear();
attribs.Add_many(Gfh_atr_.Bry__alt, alt);
attribs.Add_many(Gfh_atr_.Bry__src, url);
boolean link_attribs_is_null = false;
if (!Php_utl_.empty(options.custom_url_link)) {
link_attribs.Clear();
link_attribs.Add_many(Gfh_atr_.Bry__href, options.custom_url_link);
if (!Php_utl_.empty(options.title)) {
link_attribs.Add_many(Gfh_atr_.Bry__title, options.title);
}
if (Php_utl_.empty(options.custom_target_link)) {
link_attribs.Add_many(Gfh_atr_.Bry__target, options.custom_target_link);
}
else if (Php_utl_.empty(options.parser_extlink_target)) {
link_attribs.Add_many(Gfh_atr_.Bry__target, options.parser_extlink_target);
}
if (Php_utl_.empty(options.parser_extlink_rel)) {
link_attribs.Add_many(Gfh_atr_.Bry__rel, options.parser_extlink_rel);
}
}
else if (!Php_utl_.empty(options.custom_title_link)) {
// byte[] title = options.custom_title_link;
// link_attribs.Clear();
// link_attribs.Add_many(Gfh_atr_.Bry__href, title.Get_link_url());
// byte[] options_title = options.title;
// link_attribs.Add_many(Gfh_atr_.Bry__title, Php_utl_.empty(options_title) ? title.Get_full_text() : options_title);
}
else if (!Php_utl_.empty(options.desc_link)) {
// link_attribs = this.getDescLinkAttribs(
// empty(options['title']) ? null : options['title'],
// $query
// );
link_attribs.Clear();
this.getDescLinkAttribs(link_attribs,
Php_utl_.empty(options.title) ? null : options.title,
null);
}
else if (!Php_utl_.empty(options.file_link)) {
// link_attribs.Clear();
// link_attribs.Add_many(Gfh_atr_.Bry__href, file.Get_url());
}
else {
link_attribs_is_null = true;
if (!Php_utl_.empty(options.title)) {
attribs.Add_many(Gfh_atr_.Bry__title, options.title);
}
}
if (!Php_utl_.empty(options.no_dimensions)) {
attribs.Add_many(Gfh_atr_.Bry__width, Int_.To_bry(width));
attribs.Add_many(Gfh_atr_.Bry__height, Int_.To_bry(height));
}
if (!Php_utl_.empty(options.valign)) {
attribs.Add_many(Gfh_atr_.Bry__style, Bry_.Add(Bry__vertical_align, options.valign));
}
if (!Php_utl_.empty(options.img_cls)) {
attribs.Add_many(Gfh_atr_.Bry__class, options.img_cls);
}
if (Php_utl_.isset(options.override_height)) {
attribs.Add_many(Gfh_atr_.Bry__class, options.override_height);
}
if (Php_utl_.isset(options.override_width)) {
attribs.Add_many(Gfh_atr_.Bry__width, options.override_height);
}
// Additional densities for responsive images, if specified.
// If any of these urls is the same as src url, it'll be excluded.
// $responsiveUrls = array_diff(this.responsiveUrls, [ this.url ]);
// if (!Php_utl_.empty($responsiveUrls)) {
// $attribs['srcset'] = Html::srcSet($responsiveUrls);
// }
// XO.MW.HOOK:ThumbnailBeforeProduceHTML
Xomw_xml.Element(tmp, Gfh_tag_.Bry__img, attribs, Bry_.Empty, Bool_.Y);
Link_wrap(bfr, link_attribs_is_null ? null : link_attribs, tmp.To_bry_and_clear());
}
private static final byte[] Bry__vertical_align = Bry_.new_a7("vertical-align: ");
}

View File

@@ -0,0 +1,611 @@
/*
XOWA: the XOWA Offline Wiki Application
Copyright (C) 2012 gnosygnu@gmail.com
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package gplx.xowa.mediawiki.includes.media; import gplx.*; import gplx.xowa.*; import gplx.xowa.mediawiki.*; import gplx.xowa.mediawiki.includes.*;
import gplx.xowa.mediawiki.includes.filerepo.file.*;
import gplx.xowa.mediawiki.includes.parsers.lnkis.*;
public class Xomw_TransformationalImageHandler extends Xomw_ImageHandler { public Xomw_TransformationalImageHandler(byte[] key) {super(key);
}
/**
* @param File image
* @param array paramsVar Transform parameters. Entries with the keys 'width'
* and 'height' are the respective screen width and height, while the keys
* 'physicalWidth' and 'physicalHeight' indicate the thumbnail dimensions.
* @return boolean
*/
@Override public boolean normaliseParams(Xomw_File image, Xomw_params_handler prms) {
if (!super.normaliseParams(image, prms)) {
return false;
}
// Obtain the source, pre-rotation dimensions
int srcWidth = image.getWidth(prms.page);
int srcHeight = image.getHeight(prms.page);
// Don't make an image bigger than the source
if (prms.physicalWidth >= srcWidth) {
prms.physicalWidth = srcWidth;
prms.physicalHeight = srcHeight;
// Skip scaling limit checks if no scaling is required
// due to requested size being bigger than source.
if (!image.mustRender()) {
return true;
}
}
return true;
}
// /**
// * Extracts the width/height if the image will be scaled before rotating
// *
// * This will match the physical size/aspect ratio of the original image
// * prior to application of the rotation -- so for a portrait image that's
// * stored as raw landscape with 90-degress rotation, the resulting size
// * will be wider than it is tall.
// *
// * @param array paramsVar Parameters as returned by normaliseParams
// * @param int rotation The rotation angle that will be applied
// * @return array (width, height) array
// */
// public function extractPreRotationDimensions(paramsVar, rotation) {
// if (rotation == 90 || rotation == 270) {
// // We'll resize before rotation, so swap the dimensions again
// width = paramsVar['physicalHeight'];
// height = paramsVar['physicalWidth'];
// } else {
// width = paramsVar['physicalWidth'];
// height = paramsVar['physicalHeight'];
// }
//
// return [ width, height ];
// }
//
/**
* Create a thumbnail.
*
* This sets up various parameters, and then calls a helper method
* based on this.getScalerType in order to scale the image.
*
* @param File image
* @param String dstPath
* @param String dstUrl
* @param array paramsVar
* @param int flags
* @return MediaTransformError|ThumbnailImage|TransformParameterError
*/
@Override public Xomw_MediaTransformOutput doTransform(Xomw_File image, byte[] dstPath, byte[] dstUrl, Xomw_params_handler prms, int flags) {
// if (!this.normaliseParams(image, paramsVar)) {
// return new TransformParameterError(paramsVar);
// }
//
// // Create a parameter array to pass to the scaler
Xomw_params_scalar scalerParams = new Xomw_params_scalar();
// // The size to which the image will be resized
scalerParams.physicalWidth = prms.physicalWidth;
scalerParams.physicalHeight = prms.physicalHeight;
// 'physicalDimensions' => "{paramsVar['physicalWidth']}x{paramsVar['physicalHeight']}",
// The size of the image on the page
scalerParams.clientWidth = prms.width;
scalerParams.clientHeight = prms.height;
// Comment as will be added to the Exif of the thumbnail
// 'comment' => isset(paramsVar['descriptionUrl'])
// ? "File source: {paramsVar['descriptionUrl']}"
// : '',
// Properties of the original image
scalerParams.srcWidth = image.getWidth();
scalerParams.srcHeight = image.getHeight();
scalerParams.mimeType = image.getMimeType();
scalerParams.dstPath = dstPath;
scalerParams.dstUrl = dstUrl;
// 'interlace' => isset(paramsVar['interlace']) ? paramsVar['interlace'] : false,
// if (isset(paramsVar['quality']) && paramsVar['quality'] === 'low') {
// scalerParams['quality'] = 30;
// }
// For subclasses that might be paged.
// if (image.isMultipage() && isset(paramsVar['page'])) {
// scalerParams['page'] = intval(paramsVar['page']);
// }
// Determine scaler type
// scaler = this.getScalerType(dstPath);
//
// if (is_array(scaler)) {
// scalerName = get_class(scaler[0]);
// } else {
// scalerName = scaler;
// }
//
// wfDebug(__METHOD__ . ": creating {scalerParams['physicalDimensions']} " .
// "thumbnail at dstPath using scaler scalerName\n");
if (!image.mustRender() &&
scalerParams.physicalWidth == scalerParams.srcWidth
&& scalerParams.physicalHeight == scalerParams.srcHeight
// && !isset(scalerParams['quality'])
) {
// normaliseParams (or the user) wants us to return the unscaled image
// wfDebug(__METHOD__ . ": returning unscaled image\n");
return this.getClientScalingThumbnailImage(image, scalerParams);
}
// if (scaler == 'client') {
// // Client-side image scaling, use the source URL
// // Using the destination URL in a TRANSFORM_LATER request would be incorrect
// return this.getClientScalingThumbnailImage(image, scalerParams);
// }
//
// if (image.isTransformedLocally() && !this.isImageAreaOkForThumbnaling(image, paramsVar)) {
// global wgMaxImageArea;
// return new TransformTooBigImageAreaError(paramsVar, wgMaxImageArea);
// }
//
// if (flags & self::TRANSFORM_LATER) {
// wfDebug(__METHOD__ . ": Transforming later per flags.\n");
// newParams = [
// 'width' => scalerParams['clientWidth'],
// 'height' => scalerParams['clientHeight']
// ];
// if (isset(paramsVar['quality'])) {
// newParams['quality'] = paramsVar['quality'];
// }
// if (isset(paramsVar['page']) && paramsVar['page']) {
// newParams['page'] = paramsVar['page'];
// }
// return new Xomw_ThumbnailImage(image, dstUrl, null, newParams);
return new Xomw_ThumbnailImage(image, dstUrl, null, prms);
// }
//
// // Try to make a target path for the thumbnail
// if (!wfMkdirParents(dirname(dstPath), null, __METHOD__)) {
// wfDebug(__METHOD__ . ": Unable to create thumbnail destination " .
// "directory, falling back to client scaling\n");
//
// return this.getClientScalingThumbnailImage(image, scalerParams);
// }
//
// // Transform functions and binaries need a FS source file
// thumbnailSource = this.getThumbnailSource(image, paramsVar);
//
// // If the source isn't the original, disable EXIF rotation because it's already been applied
// if (scalerParams['srcWidth'] != thumbnailSource['width']
// || scalerParams['srcHeight'] != thumbnailSource['height']) {
// scalerParams['disableRotation'] = true;
// }
//
// scalerParams['srcPath'] = thumbnailSource['path'];
// scalerParams['srcWidth'] = thumbnailSource['width'];
// scalerParams['srcHeight'] = thumbnailSource['height'];
//
// if (scalerParams['srcPath'] === false) { // Failed to get local copy
// wfDebugLog('thumbnail',
// sprintf('Thumbnail failed on %s: could not get local copy of "%s"',
// wfHostname(), image.getName()));
//
// return new MediaTransformError('thumbnail_error',
// scalerParams['clientWidth'], scalerParams['clientHeight'],
// wfMessage('filemissing')
// );
// }
//
// // Try a hook. Called "Bitmap" for historical reasons.
// /** @var mto MediaTransformOutput */
// mto = null;
// Hooks::run('BitmapHandlerTransform', [ this, image, &scalerParams, &mto ]);
// if (!is_null(mto)) {
// wfDebug(__METHOD__ . ": Hook to BitmapHandlerTransform created an mto\n");
// scaler = 'hookaborted';
// }
//
// // scaler will return a MediaTransformError on failure, or false on success.
// // If the scaler is succesful, it will have created a thumbnail at the destination
// // path.
// if (is_array(scaler) && is_callable(scaler)) {
// // Allow subclasses to specify their own rendering methods.
// err = call_user_func(scaler, image, scalerParams);
// } else {
// switch (scaler) {
// case 'hookaborted':
// // Handled by the hook above
// err = mto.isError() ? mto : false;
// break;
// case 'im':
// err = this.transformImageMagick(image, scalerParams);
// break;
// case 'custom':
// err = this.transformCustom(image, scalerParams);
// break;
// case 'imext':
// err = this.transformImageMagickExt(image, scalerParams);
// break;
// case 'gd':
// default:
// err = this.transformGd(image, scalerParams);
// break;
// }
// }
//
// // Remove the file if a zero-byte thumbnail was created, or if there was an error
// removed = this.removeBadFile(dstPath, (boolean)err);
// if (err) {
// // transform returned MediaTransforError
// return err;
// } elseif (removed) {
// // Thumbnail was zero-byte and had to be removed
// return new MediaTransformError('thumbnail_error',
// scalerParams['clientWidth'], scalerParams['clientHeight'],
// wfMessage('unknown-error')
// );
// } elseif (mto) {
// return mto;
// } else {
// newParams = [
// 'width' => scalerParams['clientWidth'],
// 'height' => scalerParams['clientHeight']
// ];
// if (isset(paramsVar['quality'])) {
// newParams['quality'] = paramsVar['quality'];
// }
// if (isset(paramsVar['page']) && paramsVar['page']) {
// newParams['page'] = paramsVar['page'];
// }
// return new ThumbnailImage(image, dstUrl, dstPath, newParams);
// }
// return null;
}
// /**
// * Get the source file for the transform
// *
// * @param File file
// * @param array paramsVar
// * @return array Array with keys width, height and path.
// */
// protected function getThumbnailSource(file, paramsVar) {
// return file.getThumbnailSource(paramsVar);
// }
//
// /**
// * Returns what sort of scaler type should be used.
// *
// * Values can be one of client, im, custom, gd, imext, or an array
// * of Object, method-name to call that specific method.
// *
// * If specifying a custom scaler command with [ Obj, method ],
// * the method in question should take 2 parameters, a File Object,
// * and a scalerParams array with various options (See doTransform
// * for what is in scalerParams). On error it should return a
// * MediaTransformError Object. On success it should return false,
// * and simply make sure the thumbnail file is located at
// * scalerParams['dstPath'].
// *
// * If there is a problem with the output path, it returns "client"
// * to do client side scaling.
// *
// * @param String dstPath
// * @param boolean checkDstPath Check that dstPath is valid
// * @return String|Callable One of client, im, custom, gd, imext, or a Callable array.
// */
// abstract protected function getScalerType(dstPath, checkDstPath = true);
/**
* Get a ThumbnailImage that respresents an image that will be scaled
* client side
*
* @param File image File associated with this thumbnail
* @param array scalerParams Array with scaler paramsVar
* @return ThumbnailImage
*
* @todo FIXME: No rotation support
*/
private Xomw_ThumbnailImage getClientScalingThumbnailImage(Xomw_File image, Xomw_params_scalar scalerParams) {
Xomw_params_handler prms = new Xomw_params_handler();
prms.width = scalerParams.clientWidth;
prms.height = scalerParams.clientHeight;
return new Xomw_ThumbnailImage(image, image.getUrl(), null, prms);
}
// /**
// * Transform an image using ImageMagick
// *
// * This is a stub method. The real method is in BitmapHander.
// *
// * @param File image File associated with this thumbnail
// * @param array paramsVar Array with scaler paramsVar
// *
// * @return MediaTransformError Error Object if error occurred, false (=no error) otherwise
// */
// protected function transformImageMagick(image, paramsVar) {
// return this.getMediaTransformError(paramsVar, "Unimplemented");
// }
//
// /**
// * Transform an image using the Imagick PHP extension
// *
// * This is a stub method. The real method is in BitmapHander.
// *
// * @param File image File associated with this thumbnail
// * @param array paramsVar Array with scaler paramsVar
// *
// * @return MediaTransformError Error Object if error occurred, false (=no error) otherwise
// */
// protected function transformImageMagickExt(image, paramsVar) {
// return this.getMediaTransformError(paramsVar, "Unimplemented");
// }
//
// /**
// * Transform an image using a custom command
// *
// * This is a stub method. The real method is in BitmapHander.
// *
// * @param File image File associated with this thumbnail
// * @param array paramsVar Array with scaler paramsVar
// *
// * @return MediaTransformError Error Object if error occurred, false (=no error) otherwise
// */
// protected function transformCustom(image, paramsVar) {
// return this.getMediaTransformError(paramsVar, "Unimplemented");
// }
//
// /**
// * Get a MediaTransformError with error 'thumbnail_error'
// *
// * @param array paramsVar Parameter array as passed to the transform* functions
// * @param String errMsg Error message
// * @return MediaTransformError
// */
// public function getMediaTransformError(paramsVar, errMsg) {
// return new MediaTransformError('thumbnail_error', paramsVar['clientWidth'],
// paramsVar['clientHeight'], errMsg);
// }
//
// /**
// * Transform an image using the built in GD library
// *
// * This is a stub method. The real method is in BitmapHander.
// *
// * @param File image File associated with this thumbnail
// * @param array paramsVar Array with scaler paramsVar
// *
// * @return MediaTransformError Error Object if error occurred, false (=no error) otherwise
// */
// protected function transformGd(image, paramsVar) {
// return this.getMediaTransformError(paramsVar, "Unimplemented");
// }
//
// /**
// * Escape a String for ImageMagick's property input (e.g. -set -comment)
// * See InterpretImageProperties() in magick/property.c
// * @param String s
// * @return String
// */
// function escapeMagickProperty(s) {
// // Double the backslashes
// s = str_replace('\\', '\\\\', s);
// // Double the percents
// s = str_replace('%', '%%', s);
// // Escape initial - or @
// if (strlen(s) > 0 && (s[0] === '-' || s[0] === '@')) {
// s = '\\' . s;
// }
//
// return s;
// }
//
// /**
// * Escape a String for ImageMagick's input filenames. See ExpandFilenames()
// * and GetPathComponent() in magick/utility.c.
// *
// * This won't work with an initial ~ or @, so input files should be prefixed
// * with the directory name.
// *
// * Glob character unescaping is broken in ImageMagick before 6.6.1-5, but
// * it's broken in a way that doesn't involve trying to convert every file
// * in a directory, so we're better off escaping and waiting for the bugfix
// * to filter down to users.
// *
// * @param String path The file path
// * @param boolean|String scene The scene specification, or false if there is none
// * @throws MWException
// * @return String
// */
// function escapeMagickInput(path, scene = false) {
// // Die on initial metacharacters (caller should prepend path)
// firstChar = substr(path, 0, 1);
// if (firstChar === '~' || firstChar === '@') {
// throw new MWException(__METHOD__ . ': cannot escape this path name');
// }
//
// // Escape glob chars
// path = preg_replace('/[*?\[\]{}]/', '\\\\\0', path);
//
// return this.escapeMagickPath(path, scene);
// }
//
// /**
// * Escape a String for ImageMagick's output filename. See
// * InterpretImageFilename() in magick/image.c.
// * @param String path The file path
// * @param boolean|String scene The scene specification, or false if there is none
// * @return String
// */
// function escapeMagickOutput(path, scene = false) {
// path = str_replace('%', '%%', path);
//
// return this.escapeMagickPath(path, scene);
// }
//
// /**
// * Armour a String against ImageMagick's GetPathComponent(). This is a
// * helper function for escapeMagickInput() and escapeMagickOutput().
// *
// * @param String path The file path
// * @param boolean|String scene The scene specification, or false if there is none
// * @throws MWException
// * @return String
// */
// protected function escapeMagickPath(path, scene = false) {
// // Die on format specifiers (other than drive letters). The regex is
// // meant to match all the formats you get from "convert -list format"
// if (preg_match('/^([a-zA-Z0-9-]+):/', path, m)) {
// if (wfIsWindows() && is_dir(m[0])) {
// // OK, it's a drive letter
// // ImageMagick has a similar exception, see IsMagickConflict()
// } else {
// throw new MWException(__METHOD__ . ': unexpected colon character in path name');
// }
// }
//
// // If there are square brackets, add a do-nothing scene specification
// // to force a literal interpretation
// if (scene === false) {
// if (strpos(path, '[') !== false) {
// path .= '[0--1]';
// }
// } else {
// path .= "[scene]";
// }
//
// return path;
// }
//
// /**
// * Retrieve the version of the installed ImageMagick
// * You can use PHPs version_compare() to use this value
// * Value is cached for one hour.
// * @return String|boolean Representing the IM version; false on error
// */
// protected function getMagickVersion() {
// cache = MediaWikiServices::getInstance().getLocalServerObjectCache();
// method = __METHOD__;
// return cache.getWithSetCallback(
// 'imagemagick-version',
// cache::TTL_HOUR,
// function () use (method) {
// global wgImageMagickConvertCommand;
//
// cmd = wfEscapeShellArg(wgImageMagickConvertCommand) . ' -version';
// wfDebug(method . ": Running convert -version\n");
// retval = '';
// return = wfShellExec(cmd, retval);
// x = preg_match(
// '/Version: ImageMagick ([0-9]*\.[0-9]*\.[0-9]*)/', return, matches
// );
// if (x != 1) {
// wfDebug(method . ": ImageMagick version check failed\n");
// return false;
// }
//
// return matches[1];
// }
// );
// }
//
// /**
// * Returns whether the current scaler supports rotation.
// *
// * @since 1.24 No longer static
// * @return boolean
// */
// public function canRotate() {
// return false;
// }
//
// /**
// * Should we automatically rotate an image based on exif
// *
// * @since 1.24 No longer static
// * @see wgEnableAutoRotation
// * @return boolean Whether auto rotation is enabled
// */
// public function autoRotateEnabled() {
// return false;
// }
//
// /**
// * Rotate a thumbnail.
// *
// * This is a stub. See BitmapHandler::rotate.
// *
// * @param File file
// * @param array paramsVar Rotate parameters.
// * 'rotation' clockwise rotation in degrees, allowed are multiples of 90
// * @since 1.24 Is non-static. From 1.21 it was static
// * @return boolean|MediaTransformError
// */
// public function rotate(file, paramsVar) {
// return new MediaTransformError('thumbnail_error', 0, 0,
// get_class(this) . ' rotation not implemented');
// }
//
// /**
// * Returns whether the file needs to be rendered. Returns true if the
// * file requires rotation and we are able to rotate it.
// *
// * @param File file
// * @return boolean
// */
// public function mustRender(file) {
// return this.canRotate() && this.getRotation(file) != 0;
// }
//
// /**
// * Check if the file is smaller than the maximum image area for thumbnailing.
// *
// * Runs the 'BitmapHandlerCheckImageArea' hook.
// *
// * @param File file
// * @param array paramsVar
// * @return boolean
// * @since 1.25
// */
// public function isImageAreaOkForThumbnaling(file, &paramsVar) {
// global wgMaxImageArea;
//
// // For historical reasons, hook starts with BitmapHandler
// checkImageAreaHookResult = null;
// Hooks::run(
// 'BitmapHandlerCheckImageArea',
// [ file, &paramsVar, &checkImageAreaHookResult ]
// );
//
// if (!is_null(checkImageAreaHookResult)) {
// // was set by hook, so return that value
// return (boolean)checkImageAreaHookResult;
// }
//
// srcWidth = file.getWidth(paramsVar['page']);
// srcHeight = file.getHeight(paramsVar['page']);
//
// if (srcWidth * srcHeight > wgMaxImageArea
// && !(file.getMimeType() == 'image/jpeg'
// && this.getScalerType(false, false) == 'im')
// ) {
// // Only ImageMagick can efficiently downsize jpg images without loading
// // the entire file in memory
// return false;
// }
// return true;
// }
}