Intervention / image

PHP Image Processing
https://image.intervention.io
MIT License
13.89k stars 1.49k forks source link

Text wrapping #143

Closed tpavlek closed 7 months ago

tpavlek commented 10 years ago

I have an image and I would like to have a paragraph of text appearing within a certain bounded box on the image. Is there any way with the current implementation to define a bounding box and have the text wrap/resize within it to fit inside?

If not, I think this would be a useful feature to have.

Thanks

olivervogel commented 10 years ago

It's not possible at the moment, but would be useful indeed.

pascalbaljet commented 8 years ago

+1

clin407 commented 8 years ago

+1. any updates on this? Or has anyone found any interesting methods to mimic it?

nurwin commented 8 years ago

+1 when it will added to the next version?

primangesta commented 8 years ago

+1

talifhani commented 8 years ago

+1

vivg commented 8 years ago

+1

XaphanBael commented 8 years ago

+1

kalenjordan commented 7 years ago

+1

trevorgreenleaf commented 7 years ago

+1

valkirilov commented 7 years ago

+1

jayaregalinada commented 7 years ago

+1

bimal125 commented 7 years ago

+1

Anizou commented 7 years ago

+1

bubjavier commented 7 years ago

here's what I did for the meantime:

Inside my Laravel controller:

<?php

$width       = 600;
$height      = 300;
$center_x    = $width / 2;
$center_y    = $height / 2;
$max_len     = 36;
$font_size   = 30;
$font_height = 20;

$text = 'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog?';

$lines = explode("\n", wordwrap($text, $max_len)
$y     = $center_y - ((count($lines) - 1) * $font_height);
$img   = \Image::canvas($width, $height, '#777');

foreach ($lines as $line)
{
    $img->text($line, $center_x, $y, function($font) use ($font_size){
        $font->file(base_path('path/to/my/font.ttf'));
        $font->size($font_size);
        $font->color('#fdf6e3');
        $font->align('center');
        $font->valign('center');
    });

    $y += $font_height * 2;
}

return $img->response();
Dadibom commented 7 years ago

http://php.net/manual/en/function.imagettfbbox.php

sngrl commented 6 years ago

+1

garzfaust commented 6 years ago

+1

tohann commented 6 years ago

+1

marysomerville commented 6 years ago

+1

icgdoug commented 6 years ago

+1

DouFuJuShi commented 6 years ago

+1

nnquan commented 6 years ago

+1

hasanAli9t commented 6 years ago

+1

JohnnyWalkerDigital commented 6 years ago

+2

tim0991 commented 6 years ago
function getTextWith($fontSize, $text)
{
    $im = new Imagick();
    $draw = new ImagickDraw();
    $draw->setFont($this->fontPath);
    $draw->setFontSize($fontSize);
    $info = $im->queryFontMetrics($draw, $text);
    return $info['textWidth'];
}

get height by $info['textHeight']

jakeydevs commented 5 years ago

Had this issue this morning, so wrote a function to help with this:

/**
* Generate an array of "lines" our text should
* use
* 
* @param  String $text 
* @param  Number $width
* @param  Number $size (Font size)
* @param  String $font (Font path)
* @return Array [text]
*/
public function generateText($text, $width, $size, $path)
{
    //-- Helpers
    $line = [];
    $lines = [];
    //-- Looop through words
    foreach(explode(" ", $text) AS $word)
    {
        //-- Add to line
        $line[] = $word;

        //-- Create new text query
        $im = new \Imagick();
        $draw = new \ImagickDraw();
        $draw->setFont($path);
        $draw->setFontSize($size);
        $info = $im->queryFontMetrics($draw, implode(" ", $line));

        //-- Check within bounds
        if ($info['textWidth'] >= $width) 
        {
                    //-- We have gone to far!
            array_pop($line);
            $lines[] = implode(" ", $line);
                    //-- Start new line
            unset($line);
            $line[] = $word;
        }
    }

    //-- We are at the end of the string
    $lines[] = implode(" ", $line);
    return $lines;
}

I then use this by doing the following on my image:

$textLines = $this->generateText(
  'This is a image that has quite a large name so should overlap',
  700, 
  $fontSize,
  $fontPath
);

foreach($textLines AS $index => $text) 
{   
  $y = (($index * $fontSize) + $padding);
  $image->text($text, $padding, $y, function($font) use ($fontPath, $fontSize) {
    $font->file($fontPath);
    $font->size($fontSize);
    $font->color('#fdf6e3');
    $font->align('left');
    $font->valign('top');
  });
}
JohnDotOwl commented 4 years ago

+1

GioPan04 commented 3 years ago

Seriously this issue is opened from 6 years and it hasn't been fixed?!

fullstackPrincess commented 3 years ago

+1

samchen945 commented 3 years ago

Need this specific feature too now :)

ghost commented 3 years ago

+1 please

suhaboncukcu commented 3 years ago

Just to provide another alternative:

$canvas = Image::canvas(1080, 1080);

$canvas->fill('#FFFFFF');

$this->text = wordwrap('your text here', 40, PHP_EOL);

$canvas->text($this->text, 100, 100, function($font) {
    $font->size(50);
    $font->color('#000000');

    // you can log $font->countLines() to see how many lines you have
});
goodmuyis commented 2 years ago

+1

n3omaster commented 2 years ago

here's what I did for the meantime:

Inside my Laravel controller:

<?php

$width       = 600;
$height      = 300;
$center_x    = $width / 2;
$center_y    = $height / 2;
$max_len     = 36;
$font_size   = 30;
$font_height = 20;

$text = 'The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog?';

$lines = explode("\n", wordwrap($text, $max_len)
$y     = $center_y - ((count($lines) - 1) * $font_height);
$img   = \Image::canvas($width, $height, '#777');

foreach ($lines as $line)
{
    $img->text($line, $center_x, $y, function($font) use ($font_size){
        $font->file(base_path('path/to/my/font.ttf'));
        $font->size($font_size);
        $font->color('#fdf6e3');
        $font->align('center');
        $font->valign('center');
    });

    $y += $font_height * 2;
}

return $img->response();

You made my day with this 👍

AfzalSabbir commented 1 year ago

+1

bertux77 commented 1 year ago

Me habéis salvado la vida, estoy trabajando en un desarrollo web y una consultoría SEO y necesitaba generar imágenes para el open graph de forma dinámica, después de muchos intentos aquí he encontrado la solución. Muchas gracias!

Kocal commented 8 months ago

Thanks for your code @jakeydevs!

I've improved/optimized it a bit, so you can pass your FontInterface implementation and Imagick/ImagickDraw instances are initialized outside the foreach:

    private static function wrapText(string $text, int $width, FontInterface $font): array
    {
        $imagick = new \Imagick();
        $imagickDraw = new \ImagickDraw();
        $imagickDraw->setFont($font->filename());
        $imagickDraw->setFontSize($font->size());

        $line = [];
        $lines = [];

        foreach (explode(' ', $text) as $word) {
            $line[] = $word;

            $fontMetrics = $imagick->queryFontMetrics($imagickDraw, implode(' ', $line));

            // If our line doesn't fit, remove the last word and place it on a new line
            if ($fontMetrics['textWidth'] >= $width) {
                array_pop($line);
                $lines[] = implode(' ', $line);
                $line = [$word];
            }
        }

        $lines[] = implode(' ', $line);

        return $lines;
    }

Then you can easily use it like this, it will draw the text by respecting your font size and line-height:

    private function drawTitle(ImageManager $imageManager, ImageInterface $interventionImage): void
    {
        $padding = 56; // If you want some padding at left/right of your image

        $font = (new Font($this->projectDir . '/assets/fonts/Inter/static/Inter-Bold.ttf'))
            ->setSize(56)
            ->setColor('rgba(255, 255, 255, 0.85)')
            ->setLineHeight(1.6)
            ->setValignment('top');

        foreach (self::wrapText('Lorem ipsum dolor sit amet ...', $interventionImage->width() - ($padding * 2), $font) as $i => $line) {
            $interventionImage
                ->text(
                    $line,
                    $padding,
                    $padding + $i * $font->size() * $font->lineHeight(),
                    $font,
                );
        }
    }
olivervogel commented 7 months ago

Text wrapping is finally part of Intervention Image as of version 3.4. See #1292 Thank you for waiting almost 10 years. 🙈

Kocal commented 7 months ago

Thanks! :)