## Apache Zeta Components Manual :: File Source for imagemagick.php

## Source for file imagemagick.php

Documentation is available at imagemagick.php

1. <?php
2. /**
3. * File containing the ezcImageAnalyzerImagemagickHandler 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. * @package ImageAnalysis
23. * @version //autogentag//
24. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
25. * @filesource
26. */
28. /**
29. * Class to retrieve information about a given image file.
30. * This is an ezcImageAnalyzerHandler that utilizes ImageMagick to analyze
31. * image files.
32. *
33. * This ezcImageAnalyzerHandler can be configured using the
34. * Option 'binary', which must be set to the full path of the ImageMagick
35. * "identify" binary. If this option is not submitted to the
36. * {@link ezcImageAnalyzerHandler::__construct()} method, the handler will
37. * use just the name of the binary ("identify" on Unix, "identify.exe" on
38. * Windows).
39. *
40. * You can provide the options of the ezcImageAnalyzerImagemagickHandler to
41. * the {@link ezcImageAnalyzer::setHandlerClasses()}.
42. *
43. * @package ImageAnalysis
44. * @version //autogentag//
45. */
46. class ezcImageAnalyzerImagemagickHandler extends ezcImageAnalyzerHandler
47. {
48. /**
49. * The ImageMagick binary to utilize.
50. *
51. * This variable is set during call to
52. * {@link ezcImageAnalyzerImagemagickHandler::checkImagemagick()}.
53. *
54. * @var string
55. */
56. protected $binary ;
58. /**
59. * Indicates if this handler is available.
60. *
61. * The first call to
62. * {@link ezcImageAnalyzerImagemagickHandler::isAvailable()}
63. * determines this variable, which is then used as a cache for the call to
64. * {@link ezcImageAnalyzerImagemagickHandler::checkImagemagick()}.
65. *
66. * @var bool
67. */
68. protected $isAvailable ;
70. /**
71. * Mapping between ImageMagick identification strings and MIME types.
72. *
73. * ImageMagick's "identify" command returns an identification string to
74. * indicate the file type examined.
75. *
76. * This map has been handcrafted, because ImageMagick misses the
77. * possibility to determine MIME types. It misses some identification
78. * strings (mostly for file types which are absolutely rare in use
79. * or which ImageMagick is only capable to read or write, but not both.).
80. *
81. * @var array(string=>string)
82. */
83. protected $mimeMap = array(
84. 'bmp' => 'image/bmp',
85. 'bmp2' => 'image/bmp',
86. 'bmp3' => 'image/bmp',
87. 'cur' => 'image/x-win-bitmap',
88. 'dcx' => 'image/dcx',
89. 'epdf' => 'application/pdf',
90. 'epi' => 'application/postscript',
91. 'eps' => 'application/postscript',
92. 'eps2' => 'application/postscript',
93. 'eps3' => 'application/postscript',
94. 'epsf' => 'application/postscript',
95. 'epsi' => 'application/postscript',
96. 'ept' => 'application/postscript',
97. 'ept2' => 'application/postscript',
98. 'ept3' => 'application/postscript',
99. 'fax' => 'image/g3fax',
100. 'fits' => 'image/x-fits',
101. 'g3' => 'image/g3fax',
102. 'gif' => 'image/gif',
103. 'gif87' => 'image/gif',
104. 'icb' => 'application/x-icb',
105. 'ico' => 'image/x-win-bitmap',
106. 'icon' => 'image/x-win-bitmap',
107. 'jng' => 'image/jng',
108. 'jpeg' => 'image/jpeg',
109. 'jpg' => 'image/jpeg',
110. 'm2v' => 'video/mpeg2',
111. 'miff' => 'application/x-mif',
112. 'mng' => 'video/mng',
113. 'mpeg' => 'video/mpeg',
114. 'mpg' => 'video/mpeg',
115. 'otb' => 'image/x-otb',
116. 'p7' => 'image/x-xv',
117. 'palm' => 'image/x-palm',
118. 'pbm' => 'image/pbm',
119. 'pcd' => 'image/pcd',
120. 'pcds' => 'image/pcd',
121. 'pcl' => 'application/pcl',
122. 'pct' => 'image/pict',
123. 'pcx' => 'image/x-pcx',
124. 'pdb' => 'application/vnd.palm',
125. 'pdf' => 'application/pdf',
126. 'pgm' => 'image/x-pgm',
127. 'picon' => 'image/xpm',
128. 'pict' => 'image/pict',
129. 'pjpeg' => 'image/pjpeg',
130. 'png' => 'image/png',
131. 'png24' => 'image/png',
132. 'png32' => 'image/png',
133. 'png8' => 'image/png',
134. 'pnm' => 'image/pbm',
135. 'ppm' => 'image/x-ppm',
136. 'ps' => 'application/postscript',
137. 'psd' => 'image/x-photoshop',
138. 'ptif' => 'image/x-ptiff',
139. 'ras' => 'image/ras',
140. 'sgi' => 'image/sgi',
141. 'sun' => 'image/ras',
142. 'svg' => 'image/svg+xml',
143. 'svgz' => 'image/svg',
144. 'text' => 'text/plain',
145. 'tga' => 'image/tga',
146. 'tif' => 'image/tiff',
147. 'tiff' => 'image/tiff',
148. 'txt' => 'text/plain',
149. 'vda' => 'image/vda',
150. 'viff' => 'image/x-viff',
151. 'vst' => 'image/vst',
152. 'wbmp' => 'image/vnd.wap.wbmp',
153. 'xbm' => 'image/x-xbitmap',
154. 'xpm' => 'image/x-xbitmap',
155. 'xv' => 'image/x-viff',
156. 'xwd' => 'image/xwd',
157. );
159. /**
160. * MIME types this handler is capable to read.
161. *
162. * This array holds an extract of the
163. * {@link ezcImageAnalyzerHandler::$mimeMap}, listing all MIME types this
164. * handler is capable to analyze. The map is indexed by the MIME type,
165. * assigned to boolean true, to speed up hash lookups.
166. *
167. * @var array(string=>bool)
168. */
169. protected $mimeTypes = array(
170. 'application/pcl' => true,
171. 'application/pdf' => true,
172. 'application/postscript' => true,
173. 'application/vnd.palm' => true,
174. 'application/x-icb' => true,
175. 'application/x-mif' => true,
176. 'image/dcx' => true,
177. 'image/g3fax' => true,
178. 'image/gif' => true,
179. 'image/jng' => true,
180. 'image/jpeg' => true,
181. 'image/pbm' => true,
182. 'image/pcd' => true,
183. 'image/pict' => true,
184. 'image/pjpeg' => true,
185. 'image/png' => true,
186. 'image/ras' => true,
187. 'image/sgi' => true,
188. 'image/svg' => true,
189. 'image/tga' => true,
190. 'image/tiff' => true,
191. 'image/vda' => true,
192. 'image/vnd.wap.wbmp' => true,
193. 'image/vst' => true,
194. 'image/x-fits' => true,
195. 'image/x-ms-bmp' => true,
196. 'image/x-otb' => true,
197. 'image/x-palm' => true,
198. 'image/x-pcx' => true,
199. 'image/x-pgm' => true,
200. 'image/x-photoshop' => true,
201. 'image/x-ppm' => true,
202. 'image/x-ptiff' => true,
203. 'image/x-viff' => true,
204. 'image/x-win-bitmap' => true,
205. 'image/x-xbitmap' => true,
206. 'image/x-xv' => true,
207. 'image/xpm' => true,
208. 'image/xwd' => true,
209. 'text/plain' => true,
210. 'video/mng' => true,
211. 'video/mpeg' => true,
212. 'video/mpeg2' => true,
213. );
215. /**
216. * Analyzes the image type.
217. * This method analyzes image data to determine the MIME type. This method
218. * returns the MIME type of the file to analyze in lowercase letters (e.g.
219. * "image/jpeg") or false, if the images MIME type could not be determined.
220. *
221. * For a list of image types this handler will be able to analyze, see
222. * {@link ezcImageAnalyzerImagemagickHandler}.
223. *
224. * @param string $file The file to analyze.
225. * @return string|boolThe MIME type if analyzation suceeded or false.
226. */
227. public function analyzeType ( $file )
228. {
229. $format = ( ezcBaseFeatures :: os () === 'Windows' ? '"%m|"' : escapeshellarg ( '%m|' ) );
230. $parameters = '-format ' . $format . ' ' . escapeshellarg ( $file );
231. $res = ezcImageAnalyzerImagemagickHandler :: runCommand ( $parameters, $outputString, $errorString );
232. if ( $res !== 0 || $errorString !== '' )
233. {
234. return false;
235. }
236. $identifiers = explode ( '|', strtolower ( $outputString ), 2 );
237. if ( !isset( $this-> mimeMap [$identifiers[0]] ) )
238. {
239. return false;
240. }
241. return $this-> mimeMap [$identifiers[0]];
242. }
244. /**
245. * Analyze the image for detailed information.
246. *
247. * This may return various information about the image, depending on it's
248. * type. All information is collected in the struct
249. * {@link ezcImageAnalyzerData}. At least the
250. * {@link ezcImageAnalyzerData::$mime} attribute is always available, if the
251. * image type can be analyzed at all. Additionally this handler will always
252. * set the {@link ezcImageAnalyzerData::$width},
253. * {@link ezcImageAnalyzerData::$height} and
254. * {@link ezcImageAnalyzerData::$size} attributes. For detailes information
255. * on the additional data returned, see {@link ezcImageAnalyzerImagemagickHandler}.
256. *
257. * @todo Why does ImageMagick return the wrong file size on TIFF with comments?
258. * @todo Check for translucent transparency.
259. *
260. * @throws ezcImageAnalyzerFileNotProcessableException
261. * If image file can not be processed.
262. * @param string $file The file to analyze.
263. * @return ezcImageAnalyzerData
264. */
265. public function analyzeImage ( $file )
266. {
267. // Example strings returned here:
268. // JPEG (Exif without comment):
269. // string(45) "[JPEG|76383|399|600|8|59428|DirectClassRGB|]*"
270. // --------------------------------
271. // TIFF (Exif with comment):
272. // string(79) "[TIFF|108125|399|600|8|113524|DirectClassRGB|A simple comment in a TIFF file.]*"
273. // --------------------------------
274. // PNG:
275. // string(46) "[PNG|5420|160|120|8|254|DirectClassRGBMatte|]*"
276. // --------------------------------
277. // GIF (Animated):
278. // string(168) "[GIF|4100|80|50|8|38|PseudoClassRGB|]*[GIF|4100|80|50|8|21|PseudoClassRGB|]*[GIF|4100|80|50|8|17|PseudoClassRGB|Copyright 1996 DeMorgan Industries Corp.
279. //
280. // Animated Cog]*"
281. // --------------------------------
283. $formatString = ( ezcBaseFeatures :: os () === 'Windows' ? '"[%m|%b|%w|%h|%k|%r|%c]*"' : escapeshellarg ( '[%m|%b|%w|%h|%k|%r|%c]*' ) );
284. $command = '-format ' . $formatString . ' ' . escapeshellarg ( $file );
286. // Execute ImageMagick
287. $return = $this-> runCommand ( $command, $outputString, $errorString );
288. if ( $return !== 0 || $errorString !== '' )
289. {
290. throw new ezcImageAnalyzerFileNotProcessableException ( $file, "ImageMagick error: '{$errorString}'." );
291. }
293. $dataStruct = new ezcImageAnalyzerData ();
295. $rawDataArr = explode ( '*', $outputString );
296. if ( sizeof ( $rawDataArr ) === 1 )
297. {
298. throw new ezcImageAnalyzerFileNotProcessableException ( $file, "ImageMagick did not return correct formated string." );
299. }
301. // Unset last (empty) element
302. unset( $rawDataArr[ count ( $rawDataArr ) - 1] );
304. if ( sizeof ( $rawDataArr ) > 1 )
305. {
306. $dataStruct->isAnimated = true;
307. }
308. foreach ( $rawDataArr as $id => $rawData )
309. {
310. $parsedData = explode ( '|', substr ( $rawData, 1, -1 ) );
311. $dataStruct->mime = $this-> mimeMap [ strtolower ( $parsedData[0] )];
313. $dataStruct->size = filesize ( $file );
315. $dataStruct->width = max ( (int) $parsedData[2], $dataStruct->width );
316. $dataStruct->height = max ( (int) $parsedData[3], $dataStruct->height );
318. $dataStruct->isColor = $parsedData[4] > 2 ? true : false;
320. $dataStruct->transparencyType = self::TRANSPARENCY_OPAQUE;
321. if ( strpos ( $parsedData[5], 'RGBMatte' ) !== FALSE )
322. {
323. $dataStruct->transparencyType = self::TRANSPARENCY_TRANSPARENT;
324. }
326. if ( $parsedData[6] !== '' )
327. {
328. if ( $dataStruct->isAnimated && $id > 0 )
329. {
330. $dataStruct->commentList[] = $parsedData[6];
331. }
332. else
333. {
334. $dataStruct->comment = $parsedData[6];
335. $dataStruct->commentList = array( $parsedData[6] );
336. }
337. }
339. if ( $dataStruct->mime === 'image/jpeg' || $dataStruct->mime === 'image/tiff' )
340. {
341. $this-> analyzeExif ( $dataStruct, $file );
342. }
343. }
344. return $dataStruct;
345. }
347. /**
348. * Analyze Exif data contained in JPEG and TIFF images.
349. *
350. * This method analyzes the Exif data contained in JPEG and TIFF images,
351. * using ImageMagick's "identify" binary.
352. *
353. * This method tries to provide the EXIF data in a format as close as
354. * possible to the format returned by ext/EXIF {@link http://php.net/exif}.
355. *
356. * @param ezcImageAnalyzerData $data The data object to fill.
357. * @param string $file The file to analyze.
358. */
359. protected function analyzeExif ( ezcImageAnalyzerData $data, $file )
360. {
361. $tagMap = array(
362. "IFD0" => array(
363. "ImageDescription",
364. "Make",
365. "Model",
366. "Orientation",
367. "XResolution",
368. "YResolution",
369. "ResolutionUnit",
370. "Software",
371. "DateTime",
372. "YCbCrPositioning",
373. "Exif_IFD_Pointer",
374. "Copyright",
375. "UserComment",
376. ),
378. "EXIF" => array(
379. "ExposureTime",
380. "FNumber",
381. "ExposureProgram",
382. "ISOSpeedRatings",
383. "ExifVersion",
384. "DateTimeOriginal",
385. "DateTimeDigitized",
386. "ComponentsConfiguration",
387. "BrightnessValue",
388. "ExposureBiasValue",
389. "MaxApertureValue",
390. "MeteringMode",
391. "LightSource",
392. "Flash",
393. "FocalLength",
394. // ImageMagick does not grab this correct, therefore not supported
395. // "SubjectLocation",
396. "MakerNote",
397. "UserComment",
398. "FlashPixVersion",
399. "ColorSpace",
400. "ExifImageWidth",
401. "ExifImageLength",
402. "InteroperabilityOffset",
403. "FileSource",
404. "SceneType",
405. "CustomRendered",
406. "ExposureMode",
407. "WhiteBalance",
408. "DigitalZoomRatio",
409. "FocalLengthIn35mmFilm",
410. "SceneCaptureType",
411. "GainControl",
412. "Contrast",
413. "Saturation",
414. "Sharpness",
415. "SubjectDistanceRange",
416. ),
417. "INTEROP" => array(
418. "InterOperabilityIndex",
419. "InterOperabilityVersion"
420. )
421. );
423. // Retreive exif data
424. $command = '-format ' . escapeshellarg ( "%[EXIF:*]" ) . ' ' . escapeshellarg ( $file );
425. $return = $this-> runCommand ( $command, $outputString, $errorString, false );
426. if ( $return !== 0 || $errorString !== '' )
427. {
428. throw new ezcImageAnalyzerFileNotProcessableException ( $file, "ImageMagick error: '{$errorString}'." );
429. }
431. // The following is done in 2 steps to ensure the same array order as ext/exif provides.
433. // Pre-process data
434. $rawData = explode ( "\n", $outputString );
435. $dataArr = array();
436. foreach ( $rawData as $dataString )
437. {
438. $dataParts = explode ( "=", $dataString, 2 );
439. if ( sizeof ( $dataParts ) === 2 )
440. {
441. $dataArr[$dataParts[0]] = substr ( $dataParts[1], -1, 1 ) === "." ? substr ( $dataParts[1], 0, -1 ) : $dataParts[1];
442. }
443. }
444. // Some post-processing is needed because ext/exif has some different tag names
445. if ( isset( $dataArr["ExifOffset"] ) )
446. {
447. $dataArr["Exif_IFD_Pointer"] = $dataArr["ExifOffset"];
448. }
449. if ( isset( $dataArr["InteroperabilityIndex"] ) )
450. {
451. $dataArr["InterOperabilityIndex"] = $dataArr["InteroperabilityIndex"];
452. }
453. if ( isset( $dataArr["InteroperabilityVersion"] ) )
454. {
455. $dataArr["InterOperabilityVersion"] = $dataArr["InteroperabilityVersion"];
456. }
457. if ( isset( $dataArr["Artist"] ) )
458. {
459. $dataArr["Author"] = $dataArr["Artist"];
460. }
462. // Assign data to tags
463. $exifArr = array();
464. foreach ( $tagMap as $section => $tags )
465. {
466. foreach ( $tags as $tag )
467. {
468. if ( isset( $dataArr[$tag] ) )
469. {
470. // Correct types
471. switch ( true )
472. {
473. case ( ctype_digit ( $dataArr[$tag] ) && stripos ( $tag, "version" ) === false ):
474. $exifArr[$section][$tag] = (int)$dataArr[$tag];
475. break;
476. case ( is_numeric ( $dataArr[$tag] ) && stripos ( $tag, "version" ) === false ):
477. $exifArr[$section][$tag] = (float)$dataArr[$tag];
478. break;
479. default:
480. $exifArr[$section][$tag] = $dataArr[$tag];
481. break;
482. }
483. }
484. }
485. }
487. // Retreive additional data for computation
488. $imageData = getimagesize ( $file );
490. $colorCount = 0;
491. $command = '-format ' . escapeshellarg ( '%k' ) . ' ' . escapeshellarg ( $file );
492. $return = $this-> runCommand ( $command, $colorCount, $errorString );
493. if ( $return !== 0 || $errorString !== '' )
494. {
495. throw new ezcImageAnalyzerFileNotProcessableException ( $file, "ImageMagick error: '{$errorString}'." );
496. }
498. // Compute additional section ext/EXIF provides
499. $additionsArr = array();
500. $addtionsArr["FILE"]["FileName"] = basename ( $file );
501. $addtionsArr["FILE"]["FileDateTime"] = filemtime ( $file );
502. $addtionsArr["FILE"]["FileSize"] = filesize ( $file );
503. $addtionsArr["FILE"]["FileType"] = $imageData[2];
504. $addtionsArr["FILE"]["MimeType"] = $data->mime;
505. $addtionsArr["FILE"]["SectionsFound"] =
506. ( isset( $exifArr["EXIF"] ) || isset( $exifArr["IFD0"] ) ? "ANY_TAG, " : "" )
507. . implode ( ", ", array_keys ( $exifArr ) );
509. $addtionsArr["COMPUTED"]["html"] = "width=\"{$data->width}\" height=\"{$data

->height}\"";
- $addtionsArr["COMPUTED"]["Height"] = $data->height;
- $addtionsArr["COMPUTED"]["Width"] = $data->width;
- $addtionsArr["COMPUTED"]["IsColor"] = ( $colorCount < 3 ) ? 0 : 1;
- // @todo Implement if possible!
- // $addtionsArr["COMPUTED"]["ByteOrderMotorola"] = null;
- $fNumberParts = isset( $exifArr["EXIF"]["FNumber"] ) ? explode ( "/", $exifArr["EXIF"]["FNumber"] ) : null;
- if ( sizeof ( $fNumberParts ) === 2 )
- {
- $addtionsArr["COMPUTED"]["ApertureFNumber"] = sprintf ( "f/%.1f", $fNumberParts[0] / $fNumberParts[1] );
- }
- // ImageMagick resturns "..." for not set comments
- if ( isset( $exifArr["EXIF"]["UserComment"] ) )
- {
- $addtionsArr["COMPUTED"]["UserComment"] = preg_match ( "/^\.*$/", $exifArr["EXIF"]["UserComment"] ) === false ? $exifArr["EXIF"]["UserComment"] : null;
- // @todo Maybe we can determine that somehow?
- // $addtionsArr["COMPUTED"]["UserCommentEncoding"] = "UNDEFINED";
- }
- // Not available through ImageMagick
- // $addtionsArr["COMPUTED"]["Thumbnail.FileType"] = null
- // $addtionsArr["COMPUTED"]["Thumbnail.MimeType"] = null
- // Merge arrays (done here, to have consistent key order)
- $data->exif = array_merge ( $addtionsArr, $exifArr );
- }
- /**
- * Returns if the handler can analyze a given MIME type.
- *
- * This method returns if the driver is capable of analyzing a given MIME
- * type. This method should be called before trying to actually analyze an
- * image using the drivers {@link ezcImageAnalyzerHandler::analyzeImage()}
- * method.
- *
- * @param string $mime The MIME type to check for.
- * @return bool True if the handler is able to analyze the MIME type.
- */
- public function canAnalyze ( $mime )
- {
- return isset( $this-> mimeTypes [ strtolower ( $mime )] );
- }
- /**
- * Checks wether the GD handler is available on the system.
- *
- * Returns if PHP's {@link getimagesize()} function is available.
- *
- * @return bool True is the handler is available.
- */
- public function isAvailable ()
- {
- if ( !isset( $this-> isAvailable ) )
- {
- $this-> isAvailable = $this-> checkImagemagick ();
- }
- return $this-> isAvailable ;
- }
- /**
- * Checks the availability of ImageMagick on the system.
- *
- * @return bool
- */
- protected function checkImagemagick ()
- {
- if ( !isset( $this-> options ['binary'] ) )
- {
- $this-> binary = ezcBaseFeatures :: getImageIdentifyExecutable ();
- }
- else if ( file_exists ( $this-> options ['binary'] ) )
- {
- $this-> binary = $this-> options ['binary'];
- }
- return ( $this-> binary !== null );
- }
- /**
- * Run the binary registered in ezcImageAnalyzerImagemagickHandler::$binary.
- *
- * This method executes the ImageMagick binary using the applied parameter
- * string. It returns the return value of the command. The output printed
- * to STDOUT and ERROUT is available through the $stdOut and $errOut
- * parameters.
- *
- * @param string $parameters The parameters for the binary to execute.
- * @param string $stdOut The standard output.
- * @param string $errOut The error output.
- * @param bool $stripNewlines Wether to strip the newlines from STDOUT.
- * @return int The return value of the command (0 on success).
- */
- protected function runCommand ( $parameters, &$stdOut, &$errOut, $stripNewlines = true )
- {
- $command = ( ezcBaseFeatures :: os () === 'Windows' ? $this-> binary : escapeshellcmd ( $this-> binary ) )
- . ( $parameters !== '' ? ' ' . $parameters : '' );
- // Prepare to run ImageMagick command
- $descriptors = array(
- array( 'pipe', 'r' ),
- array( 'pipe', 'w' ),
- array( 'pipe', 'w' ),
- );
- // Open ImageMagick process
- $process = proc_open ( $command, $descriptors, $pipes );
- // Close STDIN pipe
- fclose ( $pipes[0] );
- // Read STDOUT
- $stdOut = '';
- do
- {
- $stdOut .= ( $stripNewlines === true ) ? rtrim ( fgets ( $pipes[1], 1024), "\n" ) : fgets ( $pipes[1], 1024 );
- } while ( ! feof ( $pipes[1] ) );
- // Read STDERR
- $errOut = '';
- do
- {
- $errOut .= rtrim ( fgets ( $pipes[2], 1024), "\n" );
- } while ( ! feof ( $pipes[2] ) );
- // Wait for process to terminate and store return value
- return proc_close ( $process );
- }
- }
- ?>
Documentation generated by phpDocumentor 1.4.3