PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/ImageConversion/src/transformation.php

https://github.com/xc/zetacomponents
PHP | 417 lines | 172 code | 24 blank | 221 comment | 20 complexity | 088a951d528fcfb8a486fca70fc963fe MD5 | raw file
  1. <?php
  2. /**
  3. * File containing the ezcImageTransformation class.
  4. *
  5. * Licensed to the Apache Software Foundation (ASF) under one
  6. * or more contributor license agreements. See the NOTICE file
  7. * distributed with this work for additional information
  8. * regarding copyright ownership. The ASF licenses this file
  9. * to you under the Apache License, Version 2.0 (the
  10. * "License"); you may not use this file except in compliance
  11. * with the License. You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing,
  16. * software distributed under the License is distributed on an
  17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18. * KIND, either express or implied. See the License for the
  19. * specific language governing permissions and limitations
  20. * under the License.
  21. *
  22. * @see ezcImageConverter
  23. *
  24. * @package ImageConversion
  25. * @version //autogentag//
  26. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  27. * @filesource
  28. */
  29. /**
  30. * Provides transformations on images using filters and MIME conversions.
  31. * Objects of this class group MIME type conversion and filtering of images
  32. * into transformations of images. Transformations can be chained by referencing
  33. * to another transformation so that multiple transformations will be produced
  34. * after each other.
  35. *
  36. * <code>
  37. * $filters = array(
  38. * new ezcImageFilter( 'scaleDownByWidth',
  39. * array(
  40. * 'width' => 100
  41. * )
  42. * ),
  43. * new ezcImageFilter( 'crop',
  44. * array(
  45. * 'x' => 0,
  46. * 'y' => 0,
  47. * 'width' => 100,
  48. * 'height' => 100,
  49. * )
  50. * ),
  51. * );
  52. * $mimeTypes = array( 'image/jpeg', 'image/png' );
  53. *
  54. * // ezcImageTransformation object returned for further manipulation
  55. * $thumbnail = $converter->createTransformation(
  56. * 'thumbnail',
  57. * $filters,
  58. * $mimeTypes
  59. * );
  60. *
  61. * $converter->transform( 'thumbnail', 'var/storage/myOriginal1.jpg',
  62. * 'var/storage/myThumbnail1' ); // res: image/jpeg
  63. * $converter->transform( 'thumbnail', 'var/storage/myOriginal2.png',
  64. * 'var/storage/myThumbnail2' ); // res: image/png
  65. * $converter->transform( 'thumbnail', 'var/storage/myOriginal3.gif',
  66. * 'var/storage/myThumbnail3' ); // res: image/.png
  67. *
  68. * // Animated GIF, will simply be copied!
  69. * $converter->transform( 'thumbnail', 'var/storage/myOriginal4.gif',
  70. * 'var/storage/myThumbnail4' ); // res: image/gif
  71. * </code>
  72. *
  73. * @see ezcImageConverter
  74. *
  75. * @package ImageConversion
  76. * @version //autogentag//
  77. */
  78. class ezcImageTransformation
  79. {
  80. /**
  81. * Array of MIME types allowed as output for this transformation.
  82. * Leave empty, for all MIME types to be allowed.
  83. *
  84. * @var array(string)
  85. */
  86. protected $mimeOut;
  87. /**
  88. * Stores the filters utilized by a transformation.
  89. *
  90. * @var array(ezcImageFilter)
  91. */
  92. protected $filters;
  93. /**
  94. * Stores the name of this transformation.
  95. *
  96. * @var string
  97. */
  98. protected $name;
  99. /**
  100. * The ezcImageConverter
  101. *
  102. * @var ezcImageConverter
  103. */
  104. protected $converter;
  105. /**
  106. * The handler last used for filtering.
  107. *
  108. * @var ezcImageHandler
  109. */
  110. protected $lastHandler;
  111. /**
  112. * Options for the final save step.
  113. *
  114. * @var ezcSaveOptions
  115. */
  116. protected $saveOptions;
  117. /**
  118. * Initialize transformation.
  119. *
  120. * @param ezcImageConverter $converter The global converter.
  121. * @param string $name Name for the transformation.
  122. * @param array(ezcImageFilter) $filters Filters to apply.
  123. * @param array(string) $mimeOut Output MIME types.
  124. * @param ezcImageSaveOptions $saveOptions Options for saving images.
  125. *
  126. * @throws ezcImageFiltersException
  127. * On invalid filter or filter settings error.
  128. * @throws ezcImageMimeTypeUnsupportedException
  129. * If the output type is unsupported.
  130. */
  131. public function __construct( ezcImageConverter $converter, $name, array $filters = array(), array $mimeOut = array(), ezcImageSaveOptions $saveOptions = null )
  132. {
  133. $this->converter = $converter;
  134. $this->name = $name;
  135. $this->setFilters( $filters );
  136. $this->setMimeOut( $mimeOut );
  137. $this->setSaveOptions( $saveOptions !== null ? $saveOptions : new ezcImageSaveOptions() );
  138. }
  139. /**
  140. * Add a filter to the conversion.
  141. * Adds a filter with the specific settings. Filters can be added either
  142. * before an existing filter or at the end (leave out $before parameter).
  143. *
  144. * @param ezcImageFilter $filter The filter definition.
  145. * @param int $before Where to add the filter
  146. * @return void
  147. *
  148. * @throws ezcImageFilterNotAvailableException
  149. * If the given filter is not available.
  150. */
  151. public function addFilter( ezcImageFilter $filter, $before = null )
  152. {
  153. if ( $this->converter->hasFilter( $filter->name ) === false )
  154. {
  155. throw new ezcImageFilterNotAvailableException( $filter->name );
  156. }
  157. if ( isset( $before ) && isset( $this->filters[$before] ) )
  158. {
  159. array_splice( $this->filters, $before, 0, array( $filter ) );
  160. return;
  161. }
  162. $this->filters[] = $filter;
  163. }
  164. /**
  165. * Determine output MIME type
  166. * Returns the MIME type that the transformation will output.
  167. *
  168. * @param string $fileIn File that should deal as input for the transformation.
  169. * @param string $mimeIn Specify the MIME type, so method does not need to.
  170. *
  171. * @return string MIME type the transformation will output.
  172. *
  173. * @throws ezcImageAnalyzerException If the input type is unsupported.
  174. */
  175. public function getOutMime( $fileIn, $mimeIn = null )
  176. {
  177. if ( !isset( $mimeIn ) )
  178. {
  179. $analyzer = new ezcImageAnalyzer( $fileIn );
  180. $mimeIn = $analyzer->mime;
  181. }
  182. $mimeOut = $this->converter->getMimeOut( $mimeIn );
  183. // Is output type allowed by this transformation? Else use first allowed one...
  184. return in_array( $mimeOut, $this->mimeOut ) ? $mimeOut : reset( $this->mimeOut );
  185. }
  186. /**
  187. * Apply the given filters for the transformation.
  188. * Applies the conversion as defined to the given file and saves it as
  189. * defined.
  190. *
  191. * @param string $fileIn The file to transform.
  192. * @param string $fileOut The file to save the transformed image to.
  193. * @return void
  194. *
  195. * @throws ezcImageTransformationException If an error occurs during the
  196. * transformation. The returned exception contains the exception
  197. * the problem resulted from in it's public $parent attribute.
  198. * @throws ezcBaseFileNotFoundException If the file you are trying to
  199. * transform does not exists.
  200. * @throws ezcBaseFilePermissionException If the file you are trying to
  201. * transform is not readable.
  202. */
  203. public function transform( $fileIn, $fileOut )
  204. {
  205. // Sanity checks
  206. if ( !is_file( $fileIn ) )
  207. {
  208. throw new ezcBaseFileNotFoundException( $fileIn );
  209. }
  210. if ( !is_readable( $fileIn ) )
  211. {
  212. throw new ezcBaseFilePermissionException( $fileIn, ezcBaseFileException::READ );
  213. }
  214. // Start atomic file operation
  215. $fileTmp = tempnam( dirname( $fileOut ) . DIRECTORY_SEPARATOR, '.'. basename( $fileOut ) );
  216. copy( $fileIn, $fileTmp );
  217. try
  218. {
  219. // MIME types
  220. $analyzer = new ezcImageAnalyzer( $fileTmp );
  221. // Do not process animated GIFs
  222. if ( $analyzer->data->isAnimated )
  223. {
  224. copy( $fileTmp, $fileOut );
  225. unlink( $fileTmp );
  226. return;
  227. }
  228. $mimeIn = $analyzer->mime;
  229. }
  230. catch ( ezcImageAnalyzerException $e )
  231. {
  232. // Clean up
  233. unlink( $fileTmp );
  234. // Rethrow
  235. throw new ezcImageTransformationException( $e );
  236. }
  237. $outMime = $this->getOutMime( $fileTmp, $mimeIn );
  238. $ref = '';
  239. // Catch exceptions for cleanup
  240. try
  241. {
  242. // Apply the filters
  243. foreach ( $this->filters as $filter )
  244. {
  245. // Avoid reopening in same handler
  246. if ( isset( $this->lastHandler ) )
  247. {
  248. if ( $this->lastHandler->hasFilter( $filter->name ) )
  249. {
  250. $this->lastHandler->applyFilter( $ref, $filter );
  251. continue;
  252. }
  253. else
  254. {
  255. // Handler does not support filter, save file
  256. $this->lastHandler->save( $ref );
  257. $this->lastHandler->close( $ref );
  258. }
  259. }
  260. // Get handler to perform filter correctly
  261. $this->lastHandler = $this->converter->getHandler( $filter->name, $mimeIn );
  262. $ref = $this->lastHandler->load( $fileTmp, $mimeIn );
  263. $this->lastHandler->applyFilter( $ref, $filter );
  264. }
  265. // When no filters are performed by a transformation, we might have no last handler here
  266. if ( !isset( $this->lastHandler ) )
  267. {
  268. $this->lastHandler = $this->converter->getHandler( null, $mimeIn, $outMime );
  269. $ref = $this->lastHandler->load( $fileTmp, $mimeIn );
  270. }
  271. // Perform conversion
  272. if ( $this->lastHandler->allowsOutput( ( $outMime ) ) )
  273. {
  274. $this->lastHandler->convert( $ref, $outMime );
  275. }
  276. else
  277. {
  278. // Close in last handler
  279. $this->lastHandler->save( $ref );
  280. $this->lastHandler->close( $ref );
  281. // Destroy invalid reference (has been closed)
  282. $ref = null;
  283. // Retreive correct handler
  284. $this->lastHandler = $this->converter->getHandler( null, $mimeIn, $outMime );
  285. // Load in new handler
  286. $ref = $this->lastHandler->load( $fileTmp );
  287. // Perform conversion
  288. $this->lastHandler->convert( $ref, $outMime );
  289. }
  290. // Everything done, save and close
  291. $this->lastHandler->save( $ref, null, null, $this->saveOptions );
  292. $this->lastHandler->close( $ref );
  293. }
  294. catch ( ezcImageException $e )
  295. {
  296. // Cleanup
  297. if ( $ref !== null )
  298. {
  299. $this->lastHandler->close( $ref );
  300. }
  301. if ( file_exists( $fileTmp ) )
  302. {
  303. unlink( $fileTmp );
  304. }
  305. $this->lastHandler = null;
  306. // Rethrow
  307. throw new ezcImageTransformationException( $e );
  308. }
  309. // Cleanup
  310. $this->lastHandler = null;
  311. // Finalize atomic file operation
  312. if ( ezcBaseFeatures::os() === 'Windows' && file_exists( $fileOut ) )
  313. {
  314. // Windows does not allows overwriting files using rename,
  315. // therefore the file is unlinked here first.
  316. if ( unlink( $fileOut ) === false )
  317. {
  318. // Cleanup
  319. unlink( $fileTmp );
  320. throw new ezcImageFileNotProcessableException( $fileOut, 'The file exists and could not be unlinked.' );
  321. }
  322. }
  323. if ( @rename( $fileTmp, $fileOut ) === false )
  324. {
  325. unlink( $fileTmp );
  326. throw new ezcImageFileNotProcessableException( $fileOut, "The temporary file {$fileTmp} could not be renamed to {$fileOut}." );
  327. }
  328. }
  329. /**
  330. * Set the filters for this transformation.
  331. * Checks if the filters defined are available and saves them to the created
  332. * transformation if everything is okay.
  333. *
  334. * @param array(ezcImageFilter) $filters Array of {@link ezcImageFilter filter objects}.
  335. * @return void
  336. *
  337. * @throws ezcImageFilterNotAvailableException
  338. * If a filter is not available.
  339. * @throws ezcBaseFileException
  340. * If the filter array contains invalid object entries.
  341. */
  342. protected function setFilters( array $filters )
  343. {
  344. foreach ( $filters as $id => $filter )
  345. {
  346. if ( !$filter instanceof ezcImageFilter )
  347. {
  348. throw new ezcBaseSettingValueException( 'filters', 'array( int => ' . get_class( $filter ) . ' )', 'array( int => ezcImageFilter )' );
  349. }
  350. if ( !$this->converter->hasFilter( $filter->name ) )
  351. {
  352. throw new ezcImageFilterNotAvailableException( $filter->name );
  353. }
  354. }
  355. $this->filters = $filters;
  356. }
  357. /**
  358. * Sets the MIME types which are allowed for output.
  359. *
  360. * @param array $mime MIME types to allow output for.
  361. * @return void
  362. *
  363. * @throws ezcImageMimeTypeUnsupportedException
  364. * If the MIME types cannot be used as output of any of the
  365. * handlers in the converter.
  366. */
  367. protected function setMimeOut( array $mime )
  368. {
  369. foreach ( $mime as $mimeType )
  370. {
  371. if ( !$this->converter->allowsOutput( $mimeType ) )
  372. {
  373. throw new ezcImageMimeTypeUnsupportedException( $mimeType, 'output' );
  374. }
  375. }
  376. $this->mimeOut = $mime;
  377. }
  378. /**
  379. * Sets the save options.
  380. * Sets the save options, that are used for the final save step of the
  381. * transformation.
  382. *
  383. * {@link ezcImageSaveOptions}
  384. *
  385. * @param ezcImageSaveOptions $options Save options.
  386. * @return void
  387. */
  388. public function setSaveOptions( ezcImageSaveOptions $options )
  389. {
  390. $this->saveOptions = $options;
  391. }
  392. }
  393. ?>