image_effects_text.inc 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. <?php
  2. /**
  3. * Implementation of image_effects_text.
  4. */
  5. /**
  6. * Real implementation of hook_help() called by image_effects_text_help().
  7. *
  8. * Experimental diagnostic page to assist locating valid fonts on the system.
  9. * Only tuned for Ubuntu so far. I've been unable do find ubiquitous tools that
  10. * provide useful font listings.'
  11. */
  12. function image_effects_text_help_inc($path, $arg) {
  13. $output = "<p>
  14. For text rendering to work on a server, we <em>must</em>
  15. know the system path to the font <em>file</em>, not just the name.
  16. Font library handling differs too much on different systems and
  17. the available PHP toolkits are unable to return good diagnostics.
  18. </p><p>
  19. On Debian/Ubuntu, you may find your fonts in and under
  20. <code>/usr/share/fonts/truetype/</code>
  21. eg <code>'/usr/share/fonts/truetype/ttf-bitstream-vera/VeraMono.ttf'</code>
  22. </p><p>
  23. On OSX, they are probably in <code>/Library/Fonts/</code>
  24. eg <code>'/Library/Fonts/Times New Roman Bold Italic.ttf'</code>
  25. </p><p>
  26. On Windows, they are probably in <code>C:\\WINDOWS\Fonts\</code>
  27. eg <code>'C:\\WINDOWS\\Fonts\\comic.ttf'</code>
  28. </p><p>
  29. Of course, this will change if you deploy to a different server!
  30. so the best approach is to place your own TTF font file inside your private
  31. or public files directory and use that. Just give the filename with the
  32. 'private://' or 'public://' scheme prefix and it should be found.
  33. </p>
  34. ";
  35. $output .= t("<p>Files directory is !files</p>", array('!files' => variable_get('file_public_path', conf_path() . '/files')));
  36. return $output;
  37. }
  38. /**
  39. * Builds the form structure for the overlay text image effect.
  40. *
  41. * Note that this is not a complete form, it only contains the portion of the
  42. * form for configuring the effect options. Therefore it does not not need to
  43. * include metadata about the effect, nor a submit button.
  44. *
  45. * @param array $data
  46. * The current configuration for this image effect.
  47. *
  48. * @return array
  49. * The form definition for this effect.
  50. */
  51. function image_effects_text_form_inc($data) {
  52. // Use of functions imagecache_file_...() creates a dependency on file utility.inc.
  53. module_load_include('inc', 'imagecache_actions', 'utility');
  54. // Use of function imagecache_rgb_form() creates a dependency on file utility-color.inc.
  55. module_load_include('inc', 'imagecache_actions', 'utility-color');
  56. // Note: we also need to check for the existence of the module: admin has
  57. // all rights, so user_acccess(...) returns TRUE even if the module is not
  58. // enabled and the permission does not exist.
  59. // A user without the 'use PHP for settings' permission (defined by the core
  60. // PHP filter module) may not:
  61. // - Select the 'PHP code' text source option if it is currently not selected.
  62. // - Change the 'PHP code' textarea.
  63. $allow_dynamic = user_access('use PHP for settings') && module_exists('php');
  64. $defaults = array(
  65. 'size' => 12,
  66. 'angle' => 0,
  67. 'xpos' => '0',
  68. 'ypos' => '0',
  69. 'halign' => 'left',
  70. 'valign' => 'bottom',
  71. 'RGB' => array('HEX' => '#000000'),
  72. 'alpha' => 100,
  73. 'fontfile' => 'lhandw.ttf',
  74. 'text_source' => 'text',
  75. 'text' => 'Hello World!',
  76. 'php' => 'return \'Hello World!\'',
  77. );
  78. $data += $defaults;
  79. $form = array(
  80. 'size' => array(
  81. '#type' => 'textfield',
  82. '#title' => t('Font size'),
  83. '#default_value' => $data['size'],
  84. '#description' => t('The font size in points. Only in GD1 this is in pixels.'),
  85. '#size' => 3,
  86. ),
  87. 'xpos' => array(
  88. '#type' => 'textfield',
  89. '#title' => t('X offset'),
  90. '#default_value' => $data['xpos'],
  91. '#description' => t('Enter an offset in pixels or use a keyword: <em>left</em>, <em>center</em>, or <em>right</em>. Syntax like <em>right-20</em> is also valid.'),
  92. '#size' => 10,
  93. ),
  94. 'ypos' => array(
  95. '#type' => 'textfield',
  96. '#title' => t('Y offset'),
  97. '#default_value' => $data['ypos'],
  98. '#description' => t('Enter an offset in pixels or use a keyword: <em>top</em>, <em>center</em>, or <em>bottom</em>. Syntax like <em>bottom-20</em> is also valid.'),
  99. '#size' => 10,
  100. ),
  101. 'halign' => array(
  102. '#type' => 'select',
  103. '#title' => t('Horizontal alignment'),
  104. '#default_value' => $data['halign'],
  105. '#description' => t('The horizontal alignment of the text around the given %xpos.', array('%xpos' => t('X offset'))),
  106. '#options' => array('left' => t('Left'), 'center' => t('Center'), 'right' => t('Right')),
  107. ),
  108. 'valign' => array(
  109. '#type' => 'select',
  110. '#title' => t('Vertical alignment'),
  111. '#default_value' => $data['valign'],
  112. '#description' => t('The vertical alignment of the text around the given %ypos.', array('%ypos' => t('Y offset'))),
  113. '#options' => array('top' => t('Top'), 'center' => t('Center'), 'bottom' => t('Bottom')),
  114. ),
  115. 'RGB' => imagecache_rgb_form($data['RGB']),
  116. 'alpha' => array(
  117. '#type' => 'textfield',
  118. '#title' => t('Opacity'),
  119. '#default_value' => $data['alpha'] ? $data['alpha'] : 100,
  120. '#size' => 3,
  121. '#description' => t('Opacity: 1-100.'),
  122. ),
  123. 'angle' => array(
  124. '#type' => 'textfield',
  125. '#title' => t('Angle'),
  126. '#default_value' => $data['angle'],
  127. '#description' => t('Angle: The angle in degrees, with 0 degrees being left-to-right reading text. Higher values represent a counter-clockwise rotation. For example, a value of 90 would result in bottom-to-top reading text.'),
  128. '#size' => 3,
  129. ),
  130. 'fontfile' => array(
  131. '#type' => 'textfield',
  132. '#title' => t('Font file name'),
  133. '#default_value' => $data['fontfile'],
  134. '#description' => imagecache_actions_file_field_description(),
  135. '#element_validate' => array('imagecache_actions_validate_file'),
  136. ),
  137. 'text_help' => array(
  138. '#type' => 'item',
  139. '#title' => t('Text'),
  140. '#description' => t('<p>Select the source of the text:</p>
  141. <ul>
  142. <li><strong>Image alt</strong>: the alt text of an image field referring to this image is taken.</li>
  143. <li><strong>Image title</strong>: the title text of an image field referring to this image is taken.</li>
  144. <li><strong>Static text</strong>: a text that will be the same for each image, e.g. a copyright message. You can define the text in the text field below the drop down.</li>
  145. <li><strong>PHP code</strong>: a piece of PHP code that returns the text to display. You can define the PHP code in the text area below the drop down. You will need the \'%use_php\' permission, defined by the \'PHP filter\' module.</li>
  146. </ul>
  147. <p>See the help for an extensive explanation of the possibilities.</p>',
  148. array('%use_php' => t('Use PHP for settings'))),
  149. ),
  150. 'text_source' => array(
  151. '#type' => 'select',
  152. '#title' => t('Text source'),
  153. '#default_value' => $data['text_source'],
  154. '#options' => array('alt' => t('Image alt'), 'title' => t('Image title'), 'text' => t('Static text'), 'php' => t('PHP code')),
  155. ),
  156. 'text' => array(
  157. '#type' => 'textfield',
  158. '#title' => t('Text'),
  159. '#default_value' => $data['text'],
  160. '#states' => array(
  161. 'visible' => array(':input[name="data[text_source]"]' => array('value' => 'text')),
  162. ),
  163. ),
  164. 'php' => array(
  165. '#type' => 'textarea',
  166. '#rows' => 5,
  167. '#title' => t('PHP code'),
  168. '#default_value' => $data['php'],
  169. '#disabled' => !$allow_dynamic,
  170. '#states' => array(
  171. 'visible' => array(':input[name="data[text_source]"]' => array('value' => 'php')),
  172. ),
  173. ),
  174. );
  175. if (!$allow_dynamic && $data['text_source'] !== 'php') {
  176. unset($form['text_fieldset']['text_source']['#options']['php']);
  177. }
  178. $form['#element_validate'][] = 'image_effects_text_form_validate';
  179. return $form;
  180. }
  181. /**
  182. * Element validation callback for the effect form.
  183. * @see http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/7#element_validate
  184. */
  185. function image_effects_text_form_validate($element, &$form_state, $form) {
  186. if (!is_numeric($element['size']['#value']) || $element['size']['#value'] <= 0) {
  187. form_error($element['size'], t('%field must be a positive number.', array('%field' => t('Font size'))));
  188. }
  189. if (!is_numeric($element['alpha']['#value']) || $element['alpha']['#value'] < 0 || $element['alpha']['#value'] > 100) {
  190. form_error($element['alpha'], t('%field must be a number between 1 and 100.', array('%field' => t('Opacity'))));
  191. }
  192. if (!is_numeric($element['angle']['#value'])) {
  193. form_error($element['angle'], t('%field must be a number.', array('%field' => t('Angle'))));
  194. }
  195. }
  196. /**
  197. * Implementation of theme_hook() for text image effect.
  198. *
  199. * @param array $variables
  200. * An associative array containing:
  201. * - data: The current configuration for this resize effect.
  202. *
  203. * @return string
  204. * The HTML for the summary of a text image effect.
  205. * @ingroup themeable
  206. */
  207. function theme_image_effects_text_summary($variables) {
  208. $data = $variables['data'];
  209. switch ($data['text_source']) {
  210. case 'alt':
  211. $text = 'image alt';
  212. break;
  213. case 'title':
  214. $text = 'image title';
  215. break;
  216. case 'text':
  217. $text = $data['text'];
  218. break;
  219. case 'php':
  220. $text = 'PHP code';
  221. break;
  222. }
  223. return 'Text: ' . $text . '; Position: ' . $data['xpos'] . ',' . $data['ypos'] . '; Alignment: ' . $data['halign'] . ',' . $data['valign'];
  224. }
  225. /**
  226. * (Real implementation of) Image effect callback; Overlay text on an image
  227. * resource.
  228. *
  229. * @param object $image
  230. * An image object returned by image_load().
  231. *
  232. * @param array $data
  233. * An array of attributes to use when performing the resize effect with the
  234. * following items:
  235. * - "width": An integer representing the desired width in pixels.
  236. * - "height": An integer representing the desired height in pixels.
  237. *
  238. * @return boolean
  239. * true on success, false on failure to apply the effect.
  240. */
  241. function image_effects_text_effect_inc($image, $data) {
  242. // Use of imagecache_actions_hex2rgba() ,the imagecache_file_...() functions,
  243. // and imagecache_actions_get_image_context() create a dependency on
  244. // file utility.inc.
  245. module_load_include('inc', 'imagecache_actions', 'utility');
  246. // Massage the data and pass it on to the toolkit dependent part.
  247. // Start with a straight copy.
  248. $params = $data;
  249. // Find out where the font file is located and if it is readable.
  250. $params['fontpath'] = imagecache_actions_find_file($data['fontfile']);
  251. if ($params['fontpath'] === FALSE) {
  252. drupal_set_message(t("Failed to locate the requested font %fontfile. Cannot overlay text onto image.", array('%fontfile' => $data['fontfile'])), 'error');
  253. return FALSE;
  254. }
  255. // Get the text to overlay.
  256. $params['text'] = image_effects_text_get_text($image, $params);
  257. if ($params['text'] === FALSE) {
  258. drupal_set_message(t("Failed to evaluate text (is the 'PHP Filter' module enabled?). Not overlaying text."), 'error');
  259. return FALSE;
  260. }
  261. // Parse offsets
  262. $params['xpos'] = image_effects_text_get_offset($data['xpos'], $image->info['width'], $image->info['height'], $image->info['width']);
  263. $params['ypos'] = image_effects_text_get_offset($data['ypos'], $image->info['width'], $image->info['height'], $image->info['height']);
  264. // Convert color from hex (as it is stored in the UI).
  265. $params['RGB'] = $data['RGB'];
  266. if($params['RGB']['HEX'] && $deduced = imagecache_actions_hex2rgba($params['RGB']['HEX'])) {
  267. $params['RGB'] += $deduced;
  268. }
  269. // Make int's of various parameters
  270. $params['size'] = (int) $params['size'];
  271. $params['xpos'] = (int) $params['xpos'];
  272. $params['ypos'] = (int) $params['ypos'];
  273. // Hand over to toolkit
  274. return image_toolkit_invoke('image_effects_text', $image, array($params));
  275. }
  276. /**
  277. * GD toolkit specific implementation of this image effect.
  278. *
  279. * @param object $image
  280. * @param array $params
  281. * An array containing the parameters for this effect.
  282. *
  283. * @return bool
  284. * true on success, false otherwise.
  285. */
  286. function image_gd_image_effects_text($image, $params) {
  287. // Convert color and alpha to GD alpha and color value.
  288. // GD alpha value: 0 = opaque, 127 = transparent.
  289. $params['alpha'] = (int) ((1 - ($params['alpha'] / 100)) * 127);
  290. $color = imagecolorallocatealpha($image->resource, $params['RGB']['red'], $params['RGB']['green'], $params['RGB']['blue'], $params['alpha']);
  291. if ($color !== FALSE) {
  292. $bounds = NULL;
  293. // Adjust Y position for vertical alignment (if different from bottom).
  294. if ($params['valign'] !== 'bottom') {
  295. // Get bounding box.
  296. // PHP Manual: "This function requires both the GD library and the » FreeType library."
  297. // So it is not more demanding than imagettftext, which we need anyway.
  298. $bounds = imagettfbbox($params['size'], 0, $params['fontpath'], $params['text']);
  299. if ($bounds === FALSE) {
  300. drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment settings.'), 'warning');
  301. }
  302. else {
  303. // Get height of bounding box.
  304. $height = $bounds[1] - $bounds[7];
  305. // Shift ypos down (full height on bottom, half the height on center).
  306. $params['ypos'] += $params['valign'] === 'center' ? (int) ($height/2) : $height;
  307. }
  308. }
  309. // Adjust X position for horizontal alignment (if different from left).
  310. if ($params['halign'] !== 'left') {
  311. // Get bounding box.
  312. // PHP Manual: "This function requires both the GD library and the » FreeType library."
  313. // So it is not more demanding than imagettftext, which we need anyway.
  314. if ($bounds === NULL) {
  315. $bounds = imagettfbbox($params['size'], 0, $params['fontpath'], $params['text']);
  316. if ($bounds === FALSE) {
  317. drupal_set_message(t('Failed to calculate text dimensions using GD toolkit. Ignoring the alignment.'), 'warning');
  318. }
  319. }
  320. if ($bounds !== FALSE) {
  321. // Get width of bounding box.
  322. $width = $bounds[2] - $bounds[0];
  323. // Shift xpos to the left (full width on right, half the width on center).
  324. $params['xpos'] -= $params['halign'] === 'center' ? (int) ($width/2) : $width;
  325. }
  326. }
  327. // PHP Manual: "This function requires both the GD library and the » FreeType library."
  328. $bounds = imagettftext($image->resource, $params['size'], $params['angle'], $params['xpos'], $params['ypos'], $color, $params['fontpath'], $params['text']);
  329. return $bounds !== FALSE;
  330. }
  331. return FALSE;
  332. }
  333. /**
  334. * Imagemagick toolkit specific implementation of this image effect.
  335. *
  336. * Text in Imagemagick:
  337. * @link http://www.imagemagick.org/script/command-line-options.php?#draw
  338. * @link http://www.imagemagick.org/script/command-line-options.php?#annotate
  339. *
  340. * UTF-8/non-ascii characters
  341. * Though the online imagemagick manual mentions some problems with accented
  342. * characters, it worked fine for me in a Windows Vista shell. TBC on other
  343. * OS'es (including linux)
  344. * (@see: http://www.imagemagick.org/Usage/windows/#character_encoding)
  345. *
  346. * Alignment in Imagemagick:
  347. * This is not directly supported, though a justicifcation option has been
  348. * proposed: @link http://www.imagemagick.org/Usage/bugs/future/#justification.
  349. *
  350. * What we do have is the gravity option:
  351. * @link http://www.imagemagick.org/Usage/annotating/#gravity
  352. * Gravity is used to position a text, but it also automatically applies a
  353. * justification based on that placement. So we use gravity here for alignment,
  354. * but will thus have to rebase our positioning.
  355. *
  356. * Gravity |halign|valign |hpos change|vpos change
  357. * ------------------------------------------------
  358. * NorthWest left top 0 0
  359. * North center top -width/2 0
  360. * NorthEast right top -width 0
  361. * West left center 0 -height/2
  362. * Center center center -width/2 -height/2
  363. * East right center -width -height/2
  364. * SouthWest left bottom 0 -height
  365. * South center bottom -width/2 -height
  366. * SouthEast right bottom -width -height
  367. */
  368. function image_imagemagick_image_effects_text($image, $params) {
  369. static $alignments2gravity = array(
  370. 'left' => array(
  371. 'top' => array(
  372. 'gravity' => 'NorthWest',
  373. 'tx' => 0,
  374. 'ty' => 0,
  375. ),
  376. 'center' => array(
  377. 'gravity' => 'West',
  378. 'tx' => 0,
  379. 'ty' => -0.5,
  380. ),
  381. 'bottom' => array(
  382. 'gravity' => 'SouthWest',
  383. 'tx' => 0,
  384. 'ty' => 1, // reversed translation
  385. ),
  386. ),
  387. 'center' => array(
  388. 'top' => array(
  389. 'gravity' => 'North',
  390. 'tx' => -0.5,
  391. 'ty' => 0,
  392. ),
  393. 'center' => array(
  394. 'gravity' => 'Center',
  395. 'tx' => -0.5,
  396. 'ty' => -0.5,
  397. ),
  398. 'bottom' => array(
  399. 'gravity' => 'South',
  400. 'tx' => -0.5,
  401. 'ty' => 1, // reversed translation
  402. ),
  403. ),
  404. 'right' => array(
  405. 'top' => array(
  406. 'gravity' => 'NorthEast',
  407. 'tx' => 1, // reversed translation
  408. 'ty' => 0,
  409. ),
  410. 'center' => array(
  411. 'gravity' => 'East',
  412. 'tx' => 1, // reversed translation
  413. 'ty' => -0.5,
  414. ),
  415. 'bottom' => array(
  416. 'gravity' => 'SouthEast',
  417. 'tx' => 1, // reversed translation
  418. 'ty' => 1, // reversed translation
  419. ),
  420. ),
  421. );
  422. // Convert color and alpha to Imagemagick rgba color argument.
  423. $alpha = $params['alpha'] / 100;
  424. $color = 'rgba(' . $params['RGB']['red']. ',' . $params['RGB']['green'] . ',' . $params['RGB']['blue'] . ','. $alpha . ')';
  425. // Alignment
  426. $alignment_corrections = $alignments2gravity[$params['halign']][$params['valign']];
  427. $gravity = $alignment_corrections['gravity'];
  428. if ($alignment_corrections['tx'] > 0) {
  429. $params['xpos'] = (int) ($alignment_corrections['tx'] * $image->info['width'] - $params['xpos']);
  430. }
  431. else {
  432. $params['xpos'] += (int) ($alignment_corrections['tx'] * $image->info['width']);
  433. }
  434. if ($alignment_corrections['ty'] > 0) {
  435. $params['ypos'] = (int) ($alignment_corrections['ty'] * $image->info['height'] - $params['ypos']);
  436. }
  437. else {
  438. $params['ypos'] += (int) ($alignment_corrections['ty'] * $image->info['height']);
  439. }
  440. // Define the quote to use around the text. This is part of the argument of
  441. // the -draw command and thus should NOT be the shell argument enclosing
  442. // character.
  443. $quote = strstr($_SERVER['SERVER_SOFTWARE'], 'Win32') || strstr($_SERVER['SERVER_SOFTWARE'], 'IIS') ? "'" : '"';
  444. // and subsequently escape the use of that quote within the text.
  445. $text = $params['text'];
  446. $text = str_replace($quote, "\\$quote", $text);
  447. $image->ops[] = '-font ' . escapeshellarg($params['fontpath']);
  448. $image->ops[] = "-pointsize {$params['size']}";
  449. $image->ops[] = '-fill ' . escapeshellarg($color);
  450. // See issue http://drupal.org/node/1561214, Bootstrap should reset locale settings to UTF-8.
  451. setlocale(LC_ALL, 'C.UTF-8');
  452. $image->ops[] = '-draw ' . escapeshellarg("gravity $gravity translate {$params['xpos']},{$params['ypos']} rotate {$params['angle']} text 0,0 $quote$text$quote");
  453. return TRUE;
  454. }
  455. /**
  456. * UTILITY
  457. */
  458. /**
  459. * Convert a position into an offset in pixels.
  460. *
  461. * Position may be a number of additions and/or subtractions of:
  462. * - An value, positive or negative, in pixels.
  463. * - A, positive or negative, percentage (%). The given percentage of the
  464. * current dimension will be taken.
  465. * - 1 of the keywords:
  466. * * top: 0
  467. * * bottom: the height of the current image
  468. * * left: 0
  469. * * right: the width of the current image
  470. * * center: 50% (of the current dimension)
  471. * Examples:
  472. * 0, 20, -20, 90%, 33.3% + 10, right, center - 20, 300 - center, bottom - 50.
  473. * Note:
  474. * The algorithm will accept many more situations, though the result may be hard
  475. * to predict.
  476. *
  477. * @param int $width
  478. * The length of the horizontal dimension.
  479. * @param int $height
  480. * The length of the vertical dimension.
  481. * @param int $length
  482. * The length of the current dimension (should be either width or height).
  483. * @param string $position
  484. * The string defining the position.
  485. *
  486. * @return number
  487. * The computed offset in pixels.
  488. */
  489. function image_effects_text_get_offset($position, $width, $height, $length) {
  490. $value = 0;
  491. $tokens = preg_split('/ *(-|\+) */', $position, 0, PREG_SPLIT_DELIM_CAPTURE);
  492. $sign = 1;
  493. foreach ($tokens as $token) {
  494. switch ($token) {
  495. case '+';
  496. // Ignore, doesn't change the sign
  497. break;
  498. case '-';
  499. // Flip the sign.
  500. $sign = -$sign;
  501. break;
  502. case 'top':
  503. case 'left':
  504. // Actually, top and left are a no-op.
  505. $value += $sign * 0;
  506. $sign = 1;
  507. break;
  508. case 'bottom':
  509. // Use height of the image, even if this is for the horizontal position.
  510. $value += $sign * $height;
  511. $sign = 1;
  512. break;
  513. case 'right':
  514. // Use width of the image, even if this is for the vertical position.
  515. $value += $sign * $width;
  516. $sign = 1;
  517. break;
  518. case 'center':
  519. // half the current dimension as provided by $length.
  520. $value += $sign * $length/2;
  521. $sign = 1;
  522. break;
  523. default:
  524. // Value: absolute or percentage
  525. if (substr($token, -strlen('%')) === '%') {
  526. $percentage = ((float) substr($token, 0, -strlen('%'))) / 100.0;
  527. $value += $sign * ($percentage * $length);
  528. }
  529. else {
  530. $value += $sign * (float) $token;
  531. }
  532. $sign = 1;
  533. break;
  534. }
  535. }
  536. return $value;
  537. }
  538. /**
  539. * Get the text to use for this image.
  540. *
  541. * @param object $image
  542. * The image the current effect is to be applied to.
  543. * @param array $data
  544. * An array containing the effect data.
  545. *
  546. * @return string
  547. * Plain string to be placed on the image.
  548. */
  549. function image_effects_text_get_text($image, $data) {
  550. if ($data['text_source'] === 'text') {
  551. $text = $data['text'];
  552. }
  553. else {
  554. // Get context about the image.
  555. $image_context = imagecache_actions_get_image_context($image, $data);
  556. if ($data['text_source'] === 'alt' || $data['text_source'] === 'title') {
  557. // Existence of an image field is not guaranteed, so check for that first.
  558. $text = isset($image_context['image_field'][$data['text_source']]) ? $image_context['image_field'][$data['text_source']] : '';
  559. }
  560. else { // $data['text_source'] === 'php'
  561. // Process the php using php_eval (rather than eval), but with GLOBAL
  562. // variables, so they can be passed successfully.
  563. $GLOBALS['image_context'] = $image_context;
  564. $GLOBALS['image'] = $image;
  565. // We don't need to check_plain() the resulting text, as the text is not
  566. // rendered in a browser but processed on the server.
  567. $text = module_exists('php') ? php_eval('<'.'?php global $image, $image_context; ' . $data['php'] . ' ?'.'>') : '';
  568. unset($GLOBALS['image']);
  569. unset($GLOBALS['image_context']);
  570. }
  571. }
  572. return $text;
  573. }