--- textimage.module 2007-05-07 20:55:52.000000000 -0700 +++ textimage.module.wrap 2007-05-07 20:55:30.000000000 -0700 @@ -1,6 +1,32 @@ 7, '#validate' => array('_textimage_hex_validate' => array('color')), ); + $form['settings']['text']['maximum_width'] = array( + '#type' => 'textfield', + '#title' => t('Maximum width'), + '#field_suffix' => t('pixels'), + '#description' => t('Text lines wider than this will be wrapped. Leave blank to disable wrapping.'), + '#default_value' => $preset['settings']['text']['maximum_width'], + '#maxlength' => 4, + '#size' => 4, + ); $form['settings']['text']['margin_top'] = array( '#type' => 'textfield', '#title' => t('Text Margin Top'), @@ -647,24 +682,36 @@ $text = str_replace('_', ' ', $text); $font = variable_get('textimage_fonts_path', '') . '/' . $preset['settings']['text']['font']; - - $size = $preset['settings']['text']['size']; - $angle = $preset['settings']['text']['angle'] ? $preset['settings']['text']['angle'] : 0; - $color = $preset['settings']['text']['color']; - $case = $preset['settings']['text']['case']; - $stroke_width = $preset['settings']['text']['stroke_width']; - $stroke_color = $preset['settings']['text']['stroke_color'] ? $preset['settings']['text']['stroke_color'] : '#000000'; - - $margin_top = $preset['settings']['text']['margin_top'] ? $preset['settings']['text']['margin_top'] : 0; - $margin_right = $preset['settings']['text']['margin_right'] ? $preset['settings']['text']['margin_right'] : 0; - $margin_bottom = $preset['settings']['text']['margin_bottom'] ? $preset['settings']['text']['margin_bottom'] : 0; - $margin_left = $preset['settings']['text']['margin_left'] ? $preset['settings']['text']['margin_left'] : 0; - - $back_color = $preset['settings']['background']['color']; - $back_image = $preset['settings']['background']['image']; - $back_xoffset = $preset['settings']['background']['xoffset'] ? $preset['settings']['background']['xoffset'] : 0; - $back_yoffset = $preset['settings']['background']['yoffset'] ? $preset['settings']['background']['yoffset'] : 0; + // Fill in default preset settings. + $defaults = array( + 'text' => array( + 'size' => '', + 'angle' => 0, + 'color' => '', + 'case' => '', + 'stroke_width' => '', + 'stroke_color' => '#000000', + 'maximum_width' => 0, + 'margin_top' => 0, + 'margin_right' => 0, + 'margin_bottom' => 0, + 'margin_left' => 0, + ), + 'background' => array( + 'back_color' => '', + 'back_image' => '', + 'back_xoffset' => 0, + 'back_yoffset' => 0, + ), + ); + foreach ($defaults as $set => $values) { + $settings = $preset['settings'][$set]; + foreach ($values as $key => $value) { + $$key = $settings[$key] ? $settings[$key] : $value; + } + } + // Convert text case switch ($case) { case 'upper': @@ -691,7 +738,7 @@ } // Generate the text image - $img = textimage_text_to_image($text, $size, $font, $color, $back_color, $angle); + $img = textimage_text_to_image($text, $size, $font, $color, $back_color, $angle, $maximum_width); // Add margin if ($margin_top || $margin_right || $margin_bottom || $margin_left) { @@ -864,7 +911,80 @@ return true; } -function textimage_text_to_image($text, $fontsize, $font, $foreground_color = '#000000', $background_color = NULL, $angle) { +/** + * Helper function for wrapping text (measures width). + */ +function textimage_measure_text_width($text, $fontsize, $font) { + $box = imageTTFBbox($fontsize, $angle, $font, $text); + return abs($box[4] - $box[0]) + 4; +} + +/** + * Wrap text for rendering at a given width. + */ +function textimage_wrap_text($text, $fontsize, $font, $maximum_width) { + // State variables for the search interval + $end = 0; + $begin = 0; + $fit = $begin; + + // Note: we count in bytes for speed reasons, but maintain character boundaries. + while (true) { + // Find the next wrap point (always after trailing whitespace). + if (preg_match('/['. PREG_CLASS_PUNCTUATION .']['. PREG_CLASS_SEPARATOR .']*|['. PREG_CLASS_SEPARATOR .']+/u', $text, $match, PREG_OFFSET_CAPTURE, $end)) { + $end = $match[0][1] + strlen($match[0][0]); + } + else { + $end = strlen($text); + } + + // Fetch text, removing trailing white-space and measure it. + $line = preg_replace('/['. PREG_CLASS_SEPARATOR .']+$/u', '', substr($text, $begin, $end - $begin)); + $width = textimage_measure_text_width($line, $fontsize, $font); + + // See if $line extends past the available space. + if ($width > $maximum_width) { + // If this is the first word, we need to truncate it. + if ($fit == $begin) { + // Cut off letters until it fits. + while (strlen($line) > 0 && $width > $maximum_width) { + $line = drupal_substr($line, 0, -1); + $width = textimage_measure_text_width($line, $fontsize, $font); + } + // If no fit was found, the image is too narrow.. + $fit = strlen($line) ? $begin + strlen($line) : $end; + } + + // We have a valid fit for the next line. Insert a line-break and reset + // the search interval. + $text = substr($text, 0, $fit) ."\n". substr($text, $fit); + $end = $begin = ++$fit; + } + else { + // We can fit this text. Wait for now. + $fit = $end; + } + + if ($end == strlen($text)) { + // All text fits. No more changes are needed. + break; + } + } + return $text; +} + +/** + * Generate an image containing text with the given parameters. + * + * @return $image + * A GD image resource. + */ +function textimage_text_to_image($text, $fontsize, $font, $foreground_color = '#000000', $background_color = NULL, $angle = 0, $maximum_width = 0) { + // Perform text wrapping, if necessary. + if ($maximum_width > 0) { + $text = textimage_wrap_text($text, $fontsize, $font, $maximum_width); + } + // Get exact dimensions of text string $box = imageTTFBbox($fontsize, $angle, $font, $text); // Calculate text width and height