PageRenderTime 26ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/phase3/includes/media/Generic.php

https://github.com/brion/mediawiki-svn
PHP | 490 lines | 244 code | 55 blank | 191 comment | 37 complexity | b17e5712db5e8ee0422b61c35ae80ddd MD5 | raw file
  1. <?php
  2. /**
  3. * Media-handling base classes and generic functionality
  4. *
  5. * @file
  6. * @ingroup Media
  7. */
  8. /**
  9. * Base media handler class
  10. *
  11. * @ingroup Media
  12. */
  13. abstract class MediaHandler {
  14. const TRANSFORM_LATER = 1;
  15. /**
  16. * Instance cache
  17. */
  18. static $handlers = array();
  19. /**
  20. * Get a MediaHandler for a given MIME type from the instance cache
  21. */
  22. static function getHandler( $type ) {
  23. global $wgMediaHandlers;
  24. if ( !isset( $wgMediaHandlers[$type] ) ) {
  25. wfDebug( __METHOD__ . ": no handler found for $type.\n");
  26. return false;
  27. }
  28. $class = $wgMediaHandlers[$type];
  29. if ( !isset( self::$handlers[$class] ) ) {
  30. self::$handlers[$class] = new $class;
  31. if ( !self::$handlers[$class]->isEnabled() ) {
  32. self::$handlers[$class] = false;
  33. }
  34. }
  35. return self::$handlers[$class];
  36. }
  37. /**
  38. * Get an associative array mapping magic word IDs to parameter names.
  39. * Will be used by the parser to identify parameters.
  40. */
  41. abstract function getParamMap();
  42. /*
  43. * Validate a thumbnail parameter at parse time.
  44. * Return true to accept the parameter, and false to reject it.
  45. * If you return false, the parser will do something quiet and forgiving.
  46. */
  47. abstract function validateParam( $name, $value );
  48. /**
  49. * Merge a parameter array into a string appropriate for inclusion in filenames
  50. */
  51. abstract function makeParamString( $params );
  52. /**
  53. * Parse a param string made with makeParamString back into an array
  54. */
  55. abstract function parseParamString( $str );
  56. /**
  57. * Changes the parameter array as necessary, ready for transformation.
  58. * Should be idempotent.
  59. * Returns false if the parameters are unacceptable and the transform should fail
  60. */
  61. abstract function normaliseParams( $image, &$params );
  62. /**
  63. * Get an image size array like that returned by getimagesize(), or false if it
  64. * can't be determined.
  65. *
  66. * @param $image File: the image object, or false if there isn't one
  67. * @param $path String: the filename
  68. * @return Array
  69. */
  70. abstract function getImageSize( $image, $path );
  71. /**
  72. * Get handler-specific metadata which will be saved in the img_metadata field.
  73. *
  74. * @param $image File: the image object, or false if there isn't one
  75. * @param $path String: the filename
  76. * @return String
  77. */
  78. function getMetadata( $image, $path ) { return ''; }
  79. /**
  80. * Get a string describing the type of metadata, for display purposes.
  81. */
  82. function getMetadataType( $image ) { return false; }
  83. /**
  84. * Check if the metadata string is valid for this handler.
  85. * If it returns false, Image will reload the metadata from the file and update the database
  86. */
  87. function isMetadataValid( $image, $metadata ) { return true; }
  88. /**
  89. * Get a MediaTransformOutput object representing an alternate of the transformed
  90. * output which will call an intermediary thumbnail assist script.
  91. *
  92. * Used when the repository has a thumbnailScriptUrl option configured.
  93. *
  94. * Return false to fall back to the regular getTransform().
  95. */
  96. function getScriptedTransform( $image, $script, $params ) {
  97. return false;
  98. }
  99. /**
  100. * Get a MediaTransformOutput object representing the transformed output. Does not
  101. * actually do the transform.
  102. *
  103. * @param $image File: the image object
  104. * @param $dstPath String: filesystem destination path
  105. * @param $dstUrl String: Destination URL to use in output HTML
  106. * @param $params Array: Arbitrary set of parameters validated by $this->validateParam()
  107. */
  108. function getTransform( $image, $dstPath, $dstUrl, $params ) {
  109. return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
  110. }
  111. /**
  112. * Get a MediaTransformOutput object representing the transformed output. Does the
  113. * transform unless $flags contains self::TRANSFORM_LATER.
  114. *
  115. * @param $image File: the image object
  116. * @param $dstPath String: filesystem destination path
  117. * @param $dstUrl String: destination URL to use in output HTML
  118. * @param $params Array: arbitrary set of parameters validated by $this->validateParam()
  119. * @param $flags Integer: a bitfield, may contain self::TRANSFORM_LATER
  120. */
  121. abstract function doTransform( $image, $dstPath, $dstUrl, $params, $flags = 0 );
  122. /**
  123. * Get the thumbnail extension and MIME type for a given source MIME type
  124. * @return array thumbnail extension and MIME type
  125. */
  126. function getThumbType( $ext, $mime, $params = null ) {
  127. return array( $ext, $mime );
  128. }
  129. /**
  130. * True if the handled types can be transformed
  131. */
  132. function canRender( $file ) { return true; }
  133. /**
  134. * True if handled types cannot be displayed directly in a browser
  135. * but can be rendered
  136. */
  137. function mustRender( $file ) { return false; }
  138. /**
  139. * True if the type has multi-page capabilities
  140. */
  141. function isMultiPage( $file ) { return false; }
  142. /**
  143. * Page count for a multi-page document, false if unsupported or unknown
  144. */
  145. function pageCount( $file ) { return false; }
  146. /**
  147. * False if the handler is disabled for all files
  148. */
  149. function isEnabled() { return true; }
  150. /**
  151. * Get an associative array of page dimensions
  152. * Currently "width" and "height" are understood, but this might be
  153. * expanded in the future.
  154. * Returns false if unknown or if the document is not multi-page.
  155. */
  156. function getPageDimensions( $image, $page ) {
  157. $gis = $this->getImageSize( $image, $image->getPath() );
  158. return array(
  159. 'width' => $gis[0],
  160. 'height' => $gis[1]
  161. );
  162. }
  163. /**
  164. * Generic getter for text layer.
  165. * Currently overloaded by PDF and DjVu handlers
  166. */
  167. function getPageText( $image, $page ) {
  168. return false;
  169. }
  170. /**
  171. * Get an array structure that looks like this:
  172. *
  173. * array(
  174. * 'visible' => array(
  175. * 'Human-readable name' => 'Human readable value',
  176. * ...
  177. * ),
  178. * 'collapsed' => array(
  179. * 'Human-readable name' => 'Human readable value',
  180. * ...
  181. * )
  182. * )
  183. * The UI will format this into a table where the visible fields are always
  184. * visible, and the collapsed fields are optionally visible.
  185. *
  186. * The function should return false if there is no metadata to display.
  187. */
  188. /**
  189. * FIXME: I don't really like this interface, it's not very flexible
  190. * I think the media handler should generate HTML instead. It can do
  191. * all the formatting according to some standard. That makes it possible
  192. * to do things like visual indication of grouped and chained streams
  193. * in ogg container files.
  194. */
  195. function formatMetadata( $image ) {
  196. return false;
  197. }
  198. /**
  199. * @todo Fixme: document this!
  200. * 'value' thingy goes into a wikitext table; it used to be escaped but
  201. * that was incompatible with previous practice of customized display
  202. * with wikitext formatting via messages such as 'exif-model-value'.
  203. * So the escaping is taken back out, but generally this seems a confusing
  204. * interface.
  205. */
  206. protected static function addMeta( &$array, $visibility, $type, $id, $value, $param = false ) {
  207. $array[$visibility][] = array(
  208. 'id' => "$type-$id",
  209. 'name' => wfMsg( "$type-$id", $param ),
  210. 'value' => $value
  211. );
  212. }
  213. function getShortDesc( $file ) {
  214. global $wgLang;
  215. $nbytes = '(' . wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
  216. $wgLang->formatNum( $file->getSize() ) ) . ')';
  217. return "$nbytes";
  218. }
  219. function getLongDesc( $file ) {
  220. global $wgUser;
  221. $sk = $wgUser->getSkin();
  222. return wfMsgExt( 'file-info', 'parseinline',
  223. $sk->formatSize( $file->getSize() ),
  224. $file->getMimeType() );
  225. }
  226. static function getGeneralShortDesc( $file ) {
  227. global $wgLang;
  228. $nbytes = '(' . wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
  229. $wgLang->formatNum( $file->getSize() ) ) . ')';
  230. return "$nbytes";
  231. }
  232. static function getGeneralLongDesc( $file ) {
  233. global $wgUser;
  234. $sk = $wgUser->getSkin();
  235. return wfMsgExt( 'file-info', 'parseinline',
  236. $sk->formatSize( $file->getSize() ),
  237. $file->getMimeType() );
  238. }
  239. function getDimensionsString( $file ) {
  240. return '';
  241. }
  242. /**
  243. * Modify the parser object post-transform
  244. */
  245. function parserTransformHook( $parser, $file ) {}
  246. /**
  247. * File validation hook called on upload.
  248. *
  249. * If the file at the given local path is not valid, or its MIME type does not
  250. * match the handler class, a Status object should be returned containing
  251. * relevant errors.
  252. *
  253. * @param $fileName The local path to the file.
  254. * @return Status object
  255. */
  256. function verifyUpload( $fileName ) {
  257. return Status::newGood();
  258. }
  259. /**
  260. * Check for zero-sized thumbnails. These can be generated when
  261. * no disk space is available or some other error occurs
  262. *
  263. * @param $dstPath The location of the suspect file
  264. * @param $retval Return value of some shell process, file will be deleted if this is non-zero
  265. * @return true if removed, false otherwise
  266. */
  267. function removeBadFile( $dstPath, $retval = 0 ) {
  268. if( file_exists( $dstPath ) ) {
  269. $thumbstat = stat( $dstPath );
  270. if( $thumbstat['size'] == 0 || $retval != 0 ) {
  271. wfDebugLog( 'thumbnail',
  272. sprintf( 'Removing bad %d-byte thumbnail "%s"',
  273. $thumbstat['size'], $dstPath ) );
  274. unlink( $dstPath );
  275. return true;
  276. }
  277. }
  278. return false;
  279. }
  280. }
  281. /**
  282. * Media handler abstract base class for images
  283. *
  284. * @ingroup Media
  285. */
  286. abstract class ImageHandler extends MediaHandler {
  287. function canRender( $file ) {
  288. if ( $file->getWidth() && $file->getHeight() ) {
  289. return true;
  290. } else {
  291. return false;
  292. }
  293. }
  294. function getParamMap() {
  295. return array( 'img_width' => 'width' );
  296. }
  297. function validateParam( $name, $value ) {
  298. if ( in_array( $name, array( 'width', 'height' ) ) ) {
  299. if ( $value <= 0 ) {
  300. return false;
  301. } else {
  302. return true;
  303. }
  304. } else {
  305. return false;
  306. }
  307. }
  308. function makeParamString( $params ) {
  309. if ( isset( $params['physicalWidth'] ) ) {
  310. $width = $params['physicalWidth'];
  311. } elseif ( isset( $params['width'] ) ) {
  312. $width = $params['width'];
  313. } else {
  314. throw new MWException( 'No width specified to '.__METHOD__ );
  315. }
  316. # Removed for ProofreadPage
  317. #$width = intval( $width );
  318. return "{$width}px";
  319. }
  320. function parseParamString( $str ) {
  321. $m = false;
  322. if ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
  323. return array( 'width' => $m[1] );
  324. } else {
  325. return false;
  326. }
  327. }
  328. function getScriptParams( $params ) {
  329. return array( 'width' => $params['width'] );
  330. }
  331. function normaliseParams( $image, &$params ) {
  332. $mimeType = $image->getMimeType();
  333. if ( !isset( $params['width'] ) ) {
  334. return false;
  335. }
  336. if ( !isset( $params['page'] ) ) {
  337. $params['page'] = 1;
  338. } else {
  339. if ( $params['page'] > $image->pageCount() ) {
  340. $params['page'] = $image->pageCount();
  341. }
  342. if ( $params['page'] < 1 ) {
  343. $params['page'] = 1;
  344. }
  345. }
  346. $srcWidth = $image->getWidth( $params['page'] );
  347. $srcHeight = $image->getHeight( $params['page'] );
  348. if ( isset( $params['height'] ) && $params['height'] != -1 ) {
  349. if ( $params['width'] * $srcHeight > $params['height'] * $srcWidth ) {
  350. $params['width'] = wfFitBoxWidth( $srcWidth, $srcHeight, $params['height'] );
  351. }
  352. }
  353. $params['height'] = File::scaleHeight( $srcWidth, $srcHeight, $params['width'] );
  354. if ( !$this->validateThumbParams( $params['width'], $params['height'], $srcWidth, $srcHeight, $mimeType ) ) {
  355. return false;
  356. }
  357. return true;
  358. }
  359. /**
  360. * Get a transform output object without actually doing the transform
  361. */
  362. function getTransform( $image, $dstPath, $dstUrl, $params ) {
  363. return $this->doTransform( $image, $dstPath, $dstUrl, $params, self::TRANSFORM_LATER );
  364. }
  365. /**
  366. * Validate thumbnail parameters and fill in the correct height
  367. *
  368. * @param $width Integer: specified width (input/output)
  369. * @param $height Integer: height (output only)
  370. * @param $srcWidth Integer: width of the source image
  371. * @param $srcHeight Integer: height of the source image
  372. * @param $mimeType Unused
  373. * @return false to indicate that an error should be returned to the user.
  374. */
  375. function validateThumbParams( &$width, &$height, $srcWidth, $srcHeight, $mimeType ) {
  376. $width = intval( $width );
  377. # Sanity check $width
  378. if( $width <= 0) {
  379. wfDebug( __METHOD__.": Invalid destination width: $width\n" );
  380. return false;
  381. }
  382. if ( $srcWidth <= 0 ) {
  383. wfDebug( __METHOD__.": Invalid source width: $srcWidth\n" );
  384. return false;
  385. }
  386. $height = File::scaleHeight( $srcWidth, $srcHeight, $width );
  387. return true;
  388. }
  389. function getScriptedTransform( $image, $script, $params ) {
  390. if ( !$this->normaliseParams( $image, $params ) ) {
  391. return false;
  392. }
  393. $url = $script . '&' . wfArrayToCGI( $this->getScriptParams( $params ) );
  394. $page = isset( $params['page'] ) ? $params['page'] : false;
  395. if( $image->mustRender() || $params['width'] < $image->getWidth() ) {
  396. return new ThumbnailImage( $image, $url, $params['width'], $params['height'], $page );
  397. }
  398. }
  399. function getImageSize( $image, $path ) {
  400. wfSuppressWarnings();
  401. $gis = getimagesize( $path );
  402. wfRestoreWarnings();
  403. return $gis;
  404. }
  405. function isAnimatedImage( $image ) {
  406. return false;
  407. }
  408. function getShortDesc( $file ) {
  409. global $wgLang;
  410. $nbytes = wfMsgExt( 'nbytes', array( 'parsemag', 'escape' ),
  411. $wgLang->formatNum( $file->getSize() ) );
  412. $widthheight = wfMsgHtml( 'widthheight', $wgLang->formatNum( $file->getWidth() ) ,$wgLang->formatNum( $file->getHeight() ) );
  413. return "$widthheight ($nbytes)";
  414. }
  415. function getLongDesc( $file ) {
  416. global $wgLang;
  417. return wfMsgExt('file-info-size', 'parseinline',
  418. $wgLang->formatNum( $file->getWidth() ),
  419. $wgLang->formatNum( $file->getHeight() ),
  420. $wgLang->formatSize( $file->getSize() ),
  421. $file->getMimeType() );
  422. }
  423. function getDimensionsString( $file ) {
  424. global $wgLang;
  425. $pages = $file->pageCount();
  426. $width = $wgLang->formatNum( $file->getWidth() );
  427. $height = $wgLang->formatNum( $file->getHeight() );
  428. $pagesFmt = $wgLang->formatNum( $pages );
  429. if ( $pages > 1 ) {
  430. return wfMsgExt( 'widthheightpage', 'parsemag', $width, $height, $pagesFmt );
  431. } else {
  432. return wfMsg( 'widthheight', $width, $height );
  433. }
  434. }
  435. }