-
-
Save clifgriffin/728cc3a4ce7b81fa2d8a to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* Auto Fit Text To Image | |
* | |
* Write text to image using a target width, height, and starting font size. | |
* | |
* @author Clif Griffin | |
* @url http://cgd.io/2014/auto-fit-text-to-an-image-with-php-and-wordpress | |
* | |
* @access public | |
* @param bool $canvas_image_filename (default: false) The base image. | |
* @param string $dest_filename (default: 'output.jpg') The output filename. | |
* @param string $text (default: '') The text being written. | |
* @param int $starting_font_size (default: 60) The starting (max) font size. | |
* @param int $max_height (default: 500) The maximum height in lines of text. | |
* @param int $x_pos (default: 0) X position in pixels for first line of text. | |
* @param int $y_pos (default: 0) Y position in pixels for first line of text. | |
* @param bool $font_file (default: false) Path to font file (.ttf) | |
* @param string $font_color (default: 'black') The color. Also accepts rgba values. Example: "rgba(0,0,0,0.5)" | |
* @param int $line_height_ratio (default: 1) Allows scaling the line height. Should be between 0.1 and 1. | |
* @param string $dest_format (default: 'jpg') Output format. | |
* @return bool True: success, False: failure | |
*/ | |
function autofit_text_to_image( $canvas_image_filename = false, $dest_filename = 'output.jpg', $text = '', $starting_font_size = 60, $max_width = 500, $max_height = 500, $x_pos = 0, $y_pos = 0, $font_file = false, $font_color = 'black', $line_height_ratio = 1, $dest_format = 'jpg' ) { | |
// Bail if any essential parameters are missing | |
if ( ! $canvas_image_filename || ! $dest_filename || empty($text) || ! $font_file || empty($font_color) || empty($max_width) || empty($max_height) ) return false; | |
// Do we have a valid canvas image? | |
if ( ! file_exists($canvas_image_filename) ) return; | |
$canvas_handle = fopen( $canvas_image_filename, 'rb' ); | |
// Load image into Imagick | |
$NewImage = new Imagick(); | |
$NewImage->readImageFile($canvas_handle); | |
// Instantiate Imagick utility objects | |
$draw = new ImagickDraw(); | |
$pixel = new ImagickPixel( $font_color ); | |
// Load Font | |
$font_size = $starting_font_size; | |
$draw->setFont($font_file); | |
$draw->setFontSize($font_size); | |
// Holds calculated height of lines with given font, font size | |
$total_height = 0; | |
// Run until we find a font size that doesn't exceed $max_height in pixels | |
while ( 0 == $total_height || $total_height > $max_height ) { | |
if ( $total_height > 0 ) $font_size--; // we're still over height, decrement font size and try again | |
$draw->setFontSize($font_size); | |
// Calculate number of lines / line height | |
// Props users Sarke / BMiner: http://stackoverflow.com/questions/5746537/how-can-i-wrap-text-using-imagick-in-php-so-that-it-is-drawn-as-multiline-text | |
$words = preg_split('%\s%', $text, -1, PREG_SPLIT_NO_EMPTY); | |
$lines = array(); | |
$i = 0; | |
$line_height = 0; | |
while ( count($words) > 0 ) { | |
$metrics = $NewImage->queryFontMetrics( $draw, implode(' ', array_slice($words, 0, ++$i) ) ); | |
$line_height = max( $metrics['textHeight'], $line_height ); | |
if ( $metrics['textWidth'] > $max_width || count($words) < $i ) { | |
$lines[] = implode( ' ', array_slice($words, 0, --$i) ); | |
$words = array_slice( $words, $i ); | |
$i = 0; | |
} | |
} | |
$total_height = count($lines) * $line_height * $line_height_ratio; | |
if ( $total_height === 0 ) return false; // don't run endlessly if something goes wrong | |
} | |
// Writes text to image | |
for( $i = 0; $i < count($lines); $i++ ) { | |
$NewImage->annotateImage( $draw, $x_pos, $y_pos + ($i * $line_height * $line_height_ratio), 0, $lines[$i] ); | |
} | |
$NewImage->setImageFormat($dest_format); | |
$result = $NewImage->writeImage($dest_filename); | |
return $result; | |
} |
^ same problem, subscribed
does the alignment for the text always have to be "left" ?
for very long words to work the second while loop should look like this:
if ( $metrics['textWidth'] > $max_width || count($words) < $i ) {
// this indicates long words and forces the font to decrease in the first loop
if($i == 1){
$total_height = $max_height + 1;
continue 2;
}
$lines[] = implode( ' ', array_slice($words, 0, --$i) );
$words = array_slice( $words, $i );
$i = 0;
}
Center alignment could be done with this:
$draw->setGravity(\Imagick::GRAVITY_CENTER);
But you need to adjust annotations vertical position because first line is placed right in the middle
for( $i = 0, $c = count($lines), $gravity_fix = ($total_height / 2 - $line_height / 2) ; $i < $c; $i++ )
{
$layerTextHeader->annotateImage( $draw, $x, $y + ($i * $line_height * $line_height_ratio) - $gravity_fix, 0, $lines[$i] );
}
Also I would check the $font_size for positive values - just return if $font_size < 1 right after decreasing
@konoknir Hi, Thank you for your solution. Сan you suggest solutions so that with transition to another line, you can display the text, because now everything is together. Very necessary.
@clifgriffin Thank you very much!
Сan you suggest solutions so that with transition to another line, you can display the text, because now everything is together. Very necessary.
Great script - thank you! I just have one problem and can't find a solution: If the text contains a very long word that's width is greater than the max_width the script dies on line 64. Any idea?