wCMF  3.6
 All Classes Namespaces Files Functions Variables Groups Pages
class.GraphicsUtil.php
Go to the documentation of this file.
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2014 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of any of the following licenses
7  * at your choice:
8  *
9  * - GNU Lesser General Public License (LGPL)
10  * http://www.gnu.org/licenses/lgpl.html
11  * - Eclipse Public License (EPL)
12  * http://www.eclipse.org/org/documents/epl-v10.php
13  *
14  * See the license.txt file distributed with this work for
15  * additional information.
16  *
17  * $Id: class.GraphicsUtil.php 1462 2014-02-04 23:52:27Z iherwig $
18  */
19 require_once(BASE."wcmf/3rdparty/PHPImageWorkshop/ImageWorkshop.php");
20 require_once(BASE."wcmf/3rdparty/PHPImageWorkshop/GifFrameExtractor.php");
21 require_once(BASE."wcmf/3rdparty/PHPImageWorkshop/GifCreator.php");
22 
23 /**
24  * @class GraphicsUtil
25  * @ingroup Util
26  * @brief GraphicsUtil provides support for graphic manipulation.
27  *
28  * @author ingo herwig <ingo@wemove.com>
29  */
31 {
32  var $_errorMsg = '';
33 
34  /**
35  * Get last error message.
36  * @return The error string
37  */
38  function getErrorMsg()
39  {
40  return $this->_errorMsg;
41  }
42 
43  /**
44  * Check if a given file is an image.
45  * @param imgname Name of the imagefile to check
46  * @return Boolean whether the file is an image
47  */
48  function isImage($imgname)
49  {
50  try {
51  ImageWorkshop::initFromPath($imgname);
52  return true;
53  } catch (Exception $ex) {
54  return false;
55  }
56  }
57 
58  /**
59  * Check image dimensions.
60  * @param imgname Name of the imagefile to check
61  * @param width Width of the image, -1 means don't care
62  * @param height Height of the image, -1 means don't care
63  * @param exact Boolean whether the image should match the dimension exactly or might be smaller [default: true]
64  * @return Boolean whether the image meets the dimensions, error string provided by getErrorMsg()
65  */
66  function isValidImageDimension($imgname, $width, $height, $exact=true)
67  {
68  $widthOk = ($width == -1) || $this->isValidImageWidth($imgname, $width, $exact);
69  $heightOk = ($height == -1) || $this->isValidImageHeight($imgname, $height, $exact);
70  return ($widthOk && $heightOk);
71  }
72 
73  /**
74  * Check image width.
75  * @param imgname Name of the imagefile to check
76  * @param width Width of the image
77  * @param exact Boolean whether the image width should match exactly or might be smaller [default: true]
78  * @return Boolean whether the image width meets the criteria, error string provided by getErrorMsg()
79  * @note This method returns true if the file does not exist.
80  */
81  function isValidImageWidth($imgname, $width, $exact=true)
82  {
83  try {
84  $image = ImageWorkshop::initFromPath($imgname);
85  } catch (Exception $ex) {
86  return true;
87  }
88  $imgWitdh = $image->getWidth();
89  $dimOk = ($exact && $imgWitdh == $width) || (!$exact && $imgWitdh <= $width);
90  if (!$dimOk) {
91  $constraint = $exact ? Message::get("exactly") : Message::get("smaller than");
92  $this->_errorMsg .= Message::get("Wrong image width. Image width must be %1% %2%px - actual image width is %3%px.",
93  array($constraint, $width, $imgWitdh));
94  $this->_errorMsg .= "\n";
95  }
96  return $dimOk;
97  }
98 
99  /**
100  * Check image height.
101  * @param imgname Name of the imagefile to check
102  * @param height Height of the image
103  * @param exact Boolean whether the image height should match exactly or might be smaller [default: true]
104  * @return Boolean whether the image width meets the criteria, error string provided by getErrorMsg()
105  * @note This method returns true if the file does not exist.
106  */
107  function isValidImageHeight($imgname, $height, $exact=true)
108  {
109  try {
110  $image = ImageWorkshop::initFromPath($imgname);
111  } catch (Exception $ex) {
112  return true;
113  }
114  $imgHeight = $image->getHeight();
115  $dimOk = ($exact && $imgHeight == $height) || (!$exact && $imgHeight <= $height);
116  if (!$dimOk) {
117  $constraint = $exact ? Message::get("exactly") : Message::get("smaller than");
118  $this->_errorMsg .= Message::get("Wrong image width. Image height must be %1% %2%px - actual image height is %3%px.",
119  array($constraint, $height, $imgHeight));
120  $this->_errorMsg .= "\n";
121  }
122  return $dimOk;
123  }
124 
125  /**
126  * Calculate image dimension to fit into a square, preserving the aspect ratio
127  * @param srcName The source file name
128  * @param maxDimension The maximum dimension the image should have (either width or height)
129  * @return Array with width and height value or null, on error, error string provided by getErrorMsg()
130  */
131  function fitIntoSquare($srcName, $maxDimension)
132  {
133  try {
134  $image = ImageWorkshop::initFromPath($srcName);
135  $sourceWidth = $image->getWidth();
136  $sourceHeight = $image->getHeight();
137  if ($sourceWidth < $sourceHeight) {
138  $height = $maxDimension;
139  $width = floor($sourceWidth*$height/$sourceHeight);
140  }
141  else {
142  $width = $maxDimension;
143  $height = floor($sourceHeight*$width/$sourceWidth);
144  }
145  // if image is actually smaller, leave small
146  if ($width > $sourceWidth && $height > $sourceHeigth) {
147  $width = $sourceWidth;
148  $height = $sourceHeight;
149  }
150  return array($width, $height);
151  } catch (Exception $ex) {
152  $this->_errorMsg = $ex->getMessage();
153  return null;
154  }
155  }
156 
157  /**
158  * Create a thumbnail of an image file.
159  * @param srcName The source file name
160  * @param destName The destination file name
161  * @param width The width of the thumbnail (maybe null)
162  * @param height The height of the thumbnail (maybe null)
163  * @return Boolean whether the operation succeeded, error string provided by getErrorMsg()
164  * @note: supported image formats are GIF, JPG, PNG
165  * if only width or height are given the other dimension is calculated to preserve the aspect
166  */
167  function createThumbnail($srcName, $destName, $width, $height)
168  {
169  try {
170  $keepAspect = $width === null || $height === null;
171  self::processImageFunction($srcName, $destName, "resizeInPixel", array($width, $height, $keepAspect));
172  return true;
173  } catch (Exception $ex) {
174  $this->_errorMsg = $ex->getMessage();
175  return false;
176  }
177  }
178 
179  /**
180  * Crop an image to the given size starting from the middle of a given start point.
181  * @param srcName The source file name
182  * @param destName The destination file name
183  * @param width The width of the cropped image (maybe null)
184  * @param height The height of the cropped image (maybe null)
185  * @param x The start point x coordinate (maybe null, default null)
186  * @param y The start point y coordinate (maybe null, default null)
187  * @return Boolean whether the operation succeeded, error string provided by getErrorMsg()
188  * @note: supported image formats are GIF, JPG, PNG
189  * if only width or height are given the other dimension is taken from the original image
190  */
191  function cropImage($srcName, $destName, $width, $height, $x=null, $y=null)
192  {
193  try {
194  // calculate parameters based on image (or first frame of gif animation)
195  $image = ImageWorkshop::initFromPath($srcName);
196  list($width, $height) = self::calculateSizeParams($width, $height, $image->getWidth(), $image->getHeight());
197  $x = ($x === null) ? $image->getWidth()/2 : $x;
198  $y = ($y === null) ? $image->getHeight()/2 : $y;
199 
200  self::processImageFunction($srcName, $destName, "cropInPixel", array($width, $height, $x, $y, 'LT'));
201  return true;
202  } catch (Exception $ex) {
203  $this->_errorMsg = $ex->getMessage();
204  return false;
205  }
206  }
207 
208  /**
209  * Create a black and white copy of an image.
210  * @param srcName The source file name
211  * @param destName The destination file name
212  */
213  function createBlackWhiteImage($srcName, $destName)
214  {
215  try {
216  self::processImageFunction($srcName, $destName, "applyFilter", array(IMG_FILTER_GRAYSCALE));
217  return true;
218  } catch (Exception $ex) {
219  $this->_errorMsg = $ex->getMessage();
220  return false;
221  }
222  }
223 
224  /**
225  * Process the given function on the given source image (supports animated gifs)
226  * and save the result in the given destination image.
227  * @param srcName The source file name
228  * @param destName The destination file name
229  * @param function The name of the function
230  * @param params The paremeters to be passed to the function
231  */
232  function processImageFunction($srcName, $destName, $function, $params) {
233  if (GifFrameExtractor::isAnimatedGif($srcName)) {
234  // for animated gifs we need to process each frame
235  $gfe = new GifFrameExtractor();
236  $frames = $gfe->extract($srcName);
237  $retouchedFrames = array();
238  foreach ($frames as $frame) {
239  $frameLayer = ImageWorkshop::initFromResourceVar($frame['image']);
240  call_user_func_array(array($frameLayer, $function), $params);
241  $retouchedFrames[] = $frameLayer->getResult();
242  }
243  $gc = new GifCreator();
244  $gc->create($retouchedFrames, $gfe->getFrameDurations(), 0);
245  file_put_contents($destName, $gc->getGif());
246  }
247  else {
248  // all other images
249  $image = ImageWorkshop::initFromPath($srcName);
250  call_user_func_array(array($image, $function), $params);
251  $image->save(dirname($destName), basename($destName), true, null, 100);
252  }
253  chmod($destName, 0644);
254  }
255 
256  /**
257  * Render a text to an image. Using the default parameters the text will
258  * be rendered into a box that fits the text. If the width parameter is not null and the
259  * text exceeds the width, the text will be wrapped and the height parameter will be
260  * used as lineheight.
261  * Wrapping code is from http://de.php.net/manual/de/function.imagettfbbox.php#60673
262  * @param text The text to render
263  * @param fontfile The font file to use
264  * @param fontsize The font size to use (in pixels)
265  * @param color The color to use for the text (as HEX value)
266  * @param bgcolor The color to use for the background (as HEX value)
267  * @param filename The name of the file to write to
268  * @param width The width of the image (or null if it should fit the text) [default: null]
269  * @param height The height of the image (or null if it should fit the text) [default: null]
270  * @param x The x offset of the text (or null if it should be centered) [default: null]
271  * @param y The y offset of the text (or null if the baseline should be the image border) [default: null]
272  * @param angle The angle of the text (optional) [default: 0]
273  * @return Boolean whether the operation succeeded, error string provided by getErrorMsg()
274  */
275  function renderTextTTF($text, $fontfile, $fontsize, $color, $bgcolor, $filename,
276  $width=null, $height=null, $x=null, $y=null, $angle=0)
277  {
278  try {
279  // the destination lines array
280  $dstLines = array();
281  $lineheight = $height;
282 
283  // if a width is given, we wrap the text if necessary
284  if ($width != null) {
285  // remove windows line-breaks
286  $text = str_replace("\r", '', $text);
287  // split text into "lines"
288  $srcLines = split ("\n", $text);
289  foreach ($srcLines as $currentL) {
290  $line = '';
291  // split line into words
292  $wordsTmp = split(" ", $currentL);
293  // split at hyphens
294  $words = array();
295  foreach ($wordsTmp as $word) {
296  $wordParts = split(' ', str_replace(array('-', '/'), array('- ', '/ '), $word));
297  foreach ($wordParts as $wordPart) {
298  $words[] = $wordPart;
299  }
300  }
301  for ($i=0, $count=sizeof($words); $i<$count; $i++) {
302  $word = $words[$i];
303 
304  // get the length of this line, if the word is to be included
305  list($linewidth, $lineheight) = self::getTextDimension($fontsize, $angle, $fontfile, $text);
306 
307  // check if it is too big if the word was added, if so, then move on
308  if ($linewidth > $width && !empty($line)) {
309  // add the line like it was without spaces
310  $dstLines[] = trim($line);
311  $line = '';
312  }
313  // add the trailing space only if the word does not end with a hyphen
314  // and it is not the last word
315  if (preg_match('/-$/', $word) || $i==sizeof($words)-1) {
316  $line .= $word;
317  }
318  else {
319  $line .= $word.' ';
320  }
321  }
322  // add the line when the line ends
323  $dstLines[] = trim($line);
324  }
325  // get the text dimensions
326  $textwidth = $width;
327  if ($height != null) {
328  $lineheight = $height;
329  }
330  $textheight = sizeof($dstLines)*$lineheight;
331  $height = $textheight;
332  }
333  else {
334  $dstLines[] = $text;
335  // get the text dimensions
336  list($textwidth, $textheight) = self::getTextDimension($fontsize, $angle, $fontfile, $text);
337 
338  // add 5 pixels to the width.
339  // @todo make this a parameter
340  $textwidth += 5;
341 
342  // calculate offset and dimensions
343  list($width, $height, $x, $y) = self::calculateTextParams($width, $height, $x, $y, $textwidth, $textheight);
344  }
345 
346  // create the image
347  $image = ImageWorkshop::initVirginLayer($width, $height, $bgcolor);
348 
349  // render the text onto the image
350  foreach ($dstLines as $nr => $line) {
351  // calculate offset and dimensions
352  list($width, $height, $x, $y) = self::calculateTextParams($width, $height, $x, $y, $textwidth, $textheight);
353  // print the line
354  $image->write("$line", $fontfile, $fontsize, $color, $x, $y, $angle);
355  $y += $lineheight;
356  }
357 
358  // write the image
359  $image->save(dirname($filename), basename($filename), true, null, 100);
360  chmod($filename, 0644);
361  return true;
362  } catch (Exception $ex) {
363  $this->_errorMsg = $ex->getMessage();
364  return false;
365  }
366  }
367 
368  /**
369  * Calculate the dimension of the given text
370  * @param fontsize The font size (in pixels)
371  * @param angle The angle of the characters (optional) [default: 0]
372  * @param fontfile The font file
373  * @param text The text
374  * @return An array with the width and height values
375  */
376  function getTextDimension($fontsize, $angle, $fontfile, $text)
377  {
378  list($x2, $y2, $x3, $y3, $x1, $y1, $x0, $y0) = imagettfbbox($fontsize, $angle, $fontfile, $text);
379  return array($x1-$x2, $y2-$y1);
380  }
381 
382  /**
383  * Calculate the offset of the text and the size of the image based on the given parameters
384  * @param width The width of the image (or null if it should be the textwidth)
385  * @param height The height of the image (or null if it should be the textheight)
386  * @param x The x offset of the text (or null if it should be centered)
387  * @param y The y offset of the text (or null if the baseline should be the image border)
388  * @param textwidth The width of the text
389  * @param textheight The height of the text
390  * @return An array with width, height, x, y values
391  */
392  function calculateTextParams($width, $height, $x, $y, $textwidth, $textheight)
393  {
394  // calculate dimensions
395  if ($width === null) $width = $textwidth;
396  if ($height === null) $height = $textheight;
397 
398  // calculate offset
399  if ($x === null) $x = ($width-$textwidth)/2;
400  if ($y === null) $y = $height;
401 
402  return array($width, $height, $x, $y);
403  }
404 
405  /**
406  * Calculate the size based on the image aspect, if only width or height
407  * are given.
408  * @param width The requested width (maybe null)
409  * @param height The requested height (maybe null)
410  * @param imageWidth The image's width
411  * @param imageHeight The image's height
412  * @return Array with width, height
413  */
414  function calculateSizeParams($width, $height, $imageWidth, $imageHeight)
415  {
416  if ($width == null) {
417  $width = $imageWidth/$imageHeight*$height;
418  }
419  elseif ($height == null) {
420  $height = $imageHeight/$imageWidth*$width;
421  }
422  return array(intval($width), intval($height));
423  }
424 }
425 ?>
isValidImageHeight($imgname, $height, $exact=true)
GraphicsUtil provides support for graphic manipulation.
getTextDimension($fontsize, $angle, $fontfile, $text)
fitIntoSquare($srcName, $maxDimension)
get($message, $parameters=null, $domain='', $lang='')
createBlackWhiteImage($srcName, $destName)
createThumbnail($srcName, $destName, $width, $height)
isValidImageDimension($imgname, $width, $height, $exact=true)
calculateSizeParams($width, $height, $imageWidth, $imageHeight)
calculateTextParams($width, $height, $x, $y, $textwidth, $textheight)
isValidImageWidth($imgname, $width, $exact=true)
cropImage($srcName, $destName, $width, $height, $x=null, $y=null)
renderTextTTF($text, $fontfile, $fontsize, $color, $bgcolor, $filename, $width=null, $height=null, $x=null, $y=null, $angle=0)
processImageFunction($srcName, $destName, $function, $params)