Пишем на картинках

    В течение последнего времени что-то часто стали мелькать статьи про обработку изображений на php. Скругленные края уже были, тени были, мокрый пол был, еще куча всего было.

    А вот надписей вроде еще не было. Значит будут. :-)

    Представляю вашему вниманию не большой, но в тоже время достаточно богатый возможностями класс (на основе GD) написания текста на картинках TrueType-шрифтами.

    Код класса:


    <?php

    class ttfTextOnImage
    {  
      // Качество jpg по-умолчанияю
      public   $jpegQuality = 100;      
      
      // Каталог шрифтов
      public   $ttfFontDir   = 'ttf';  
      
      private $ttfFont    = false;
      private $ttfFontSize  = false;
        
      private $hImage      = false;
      private $hColor      = false;

      public function __construct($imagePath)
      {
        if (!is_file($imagePath) || !list(,,$type) = @getimagesize($imagePath)) return false;
            
        switch ($type)
        {      
          case 1:  $this->hImage = @imagecreatefromgif($imagePath);  break;
          case 2:  $this->hImage = @imagecreatefromjpeg($imagePath);  break;
          case 3:  $this->hImage = @imagecreatefrompng($imagePath);  break;        
          default: $this->hImage = false;
        }
      }
      
      public function __destruct()
      {
        if ($this->hImage) imagedestroy($this->hImage);
      }
      
      /**
       * Устанавливает шрифт
       *
       */  
      public function setFont($font, $size = 14, $color = false, $alpha = false)
      {
        if (!is_file($font) && !is_file($font = $this->ttfFontDir.'/'.$font))
        return false;
        
        $this->ttfFont     = $font;
        $this->ttfFontSize   = $size;
        
        if ($color) $this->setColor($color, $alpha);
      }
      
      /**
       * Пишет текст
       *
       */    
      public function writeText ($x, $y, $text, $angle = 0)
      {
        if (!$this->ttfFont || !$this->hImage || !$this->hColor) return false;
        
        imagettftext(
          $this->hImage,
          $this->ttfFontSize, $angle, $x, $y + $this->ttfFontSize,
          $this->hColor, $this->ttfFont, $text);  
      }
      
      /**
       * Форматирует текст (согласно текущему установленному шрифту),
       * что бы он не вылезал за рамки ($bWidth, $bHeight)
       * Убирает слишком длинные слова
       */
      public function textFormat($bWidth, $bHeight, $text)
      {
        // Если в строке есть длинные слова, разбиваем их на более короткие
        // Разбиваем текст по строкам
        
        $strings   = explode("\n",
          preg_replace('!([^\s]{24})[^\s]!su', '\\1 ',
            str_replace(array("\r", "\t"),array("\n", ' '), $text)));        
            
        $textOut   = array(0 => '');
        $i = 0;
              
        foreach ($strings as $str)
        {
          // Уничтожаем совокупности пробелов, разбиваем по словам
          $words = array_filter(explode(' ', $str));
          
          foreach ($words as $word)
          {
            // Какие параметры у текста в строке?
            $sizes = imagettfbbox($this->ttfFontSize, 0, $this->ttfFont, $textOut[$i].$word.' ');  
            
            // Если размер линии превышает заданный, принудительно
            // перескакиваем на следующую строку
            // Иначе пишем на этой же строке
            if ($sizes[2] > $bWidth) $textOut[++$i] = $word.' '; else $textOut[$i].= $word.' ';
            
            // Если вышли за границы текста по вертикали, то заканчиваем
            if ($i*$this->ttfFontSize >= $bHeight) break(2);
          }
          
          // "Естественный" переход на новую строку
          $textOut[++$i] = ''; if ($i*$this->ttfFontSize >= $bHeight) break;
        }
        
        return implode ("\n", $textOut);
      }
      
      /**
       * Устанваливет цвет вида #34dc12
       *
       */
      public function setColor($color, $alpha = false)
      {
        if (!$this->hImage) return false;
        
        list($r, $g, $b) = array_map('hexdec', str_split(ltrim($color, '#'), 2));
        
        return $alpha === false ?
          $this->hColor = imagecolorallocate($this->hImage, $r+1, $g+1, $b+1) :
          $this->hColor = imagecolorallocatealpha($this->hImage, $r+1, $g+1, $b+1, $alpha);    
      }
      
      /**
       * Выводит картинку в файл. Тип вывода определяется из расширения.
       *
       */
      public function output ($target, $replace = true)
      {
        if (is_file ($target) && !$replace) return false;
          
        $ext = strtolower(substr($target, strrpos($target, ".") + 1));    

        switch ($ext)
        {
          case "gif":        
            imagegif ($this->hImage, $target);        
            break;
                    
          case "jpg" :
          case "jpeg":
            imagejpeg($this->hImage, $target, $this->jpegQuality);        
            break;
            
          case "png":
            imagepng($this->hImage, $target);
            break;
            
          defaultreturn false;
        }
        return true;     
      }
    }
    ?>


    * This source code was highlighted with Source Code Highlighter.


    Что он умеет, думаю разобраться не сложно, тем более что комментарии есть. Теперь пример использования:

    // Берем какую-нибудь картинку
    $ttfImg = new ttfTextOnImage('images/hlwn.jpg');
          
    // Пишем шрифтом Scrawn размером 64 пункта бордовым цветом с 80%-ой прозрачностью
    $ttfImg->setFont('files/fonts/scra.ttf', 64, "#800000", 80);      
    $ttfImg->writeText(40, 570, "Happy halloween!");

    // Шрифтом Constantin размером 15 пунктов оранжевым цветом с 90%-ой прозрачностью
    $ttfImg->setFont('files/fonts/constan.ttf', 15, "#ff8200", 90);      

    // Хотим написать много, поэтому сначала отформатируем наш текст
    $message = $ttfImg->textFormat(400, 500,
    "Хеллоуин (англ. Halloween) — преимущественно американский праздник, празднуется в ночь с 31 октября на 1 ноября.

    Также упоминается как «канун Дня всех святых». Праздник корнями уходит к старинному кельтскому празднеству Самайн."
    );

    // Пишем (чуть-чуть наклоним)
    $ttfImg->writeText(40, 100, $message, 5);

    // и вывод в файл
    $ttfImg->output('images/postcard.jpg');


    * This source code was highlighted with Source Code Highlighter.


    Ну собственно и результат:



    Ну и на последок возможности применения:
    1. Написать что-нибудь на картинке (подпись свою например)
    2. Делать «гламурные заголовки» — когда некоторые тексты делаются красивыми картинками (в вордпрессе такое есть вроде)
    3. CAPТCHA (просто нужно взять совершенно е**нутый шрифт)

    Пользуйтесь на здоровье!

    PS: Расчитано на кодировку UTF-8

    Similar posts

    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 36

      0
      полезно, пригодится в хозяйстве
        0
        полезно
          0
          спасибо, полезно
            +3
            Полгода назад, в статье "Фотошопим на php", писал о аналогичных возможностях другой библиотеки: magickwand. Там немного лучше дела со сглаживанием.
              0
              Только с момента написания статьи iMagick стал намного стабильней, приобрел нормальный объектно ориентированный интерфейс.

              Небольшой пример использования
              0
              enartemyНаверно как-то так и счетчики рисуются, да?
                +3
                ну вот… тэг неправильно закрыл =(
                  0
                  Так и рисуются… самое полезное применение это для отрисовки информеров.
                    0
                    Так много чего рисуется. Или с помощью Imagick. В отдельных случаях используются и собственные библиотеки.

                    Кстати, никто не знает, как gd и imagick работают с анимированными gif-ами? Сам не проверял, но интересно, а проверять лень. Мож кто знает?
                      0
                      GD никак. Насколько я помню выхватывает первый кадр.
                      IMagik отлично может работать. Только придется ручками каждый кадр выгребать и создавать мнослойную картинку во внутреннем формате.
                        0
                        Что-то еще кром IMagikа может делать анимированные гифы?
                  0
                  На сколько это будет проц грузить если картинок под сотню на странице…
                    +2
                    Кэширование используйте =)
                      +1
                      Когда работаешь с GD надо бояться больше не того, как загрузится проц (хотя и этого тоже), а насколько много сожрется памяти. У меня на собственом опыте было такое, что картинка 1280 на 1024 не могла обработаться, потому что gd её тупо не мог впихуть в имеющийся выделенный объем. С Imagick-ом кстати таких проблемм меньше.

                      А вообще — конечно надо кешировать.
                        0
                        Вот тут калькулятор подсказывает 1280*1024*3=3932160, т.е. почти четыре Мб оперативки.
                        Если лимит 4 Мб (встречается даже на платных хостингах), то куда-ж денешься-то.
                        Очень интересно как Imagick отработает в такой ситуации.
                      0
                      enartemy
                      3. CAPТCHA (просто нужно взять совершенно е**нутый шрифт)

                      этим ты капчу не сделаешь :), для капчи нужно использовать одновременно несколько шрифтов, смещать и менять угол, каждого символа… вот тогда и будет самая настоящая капча :)… а и да шумы создавать в бекграунде…
                      кроме этого капча должа сама генерится из выбранных букв (abcdefghijkmnpqrstuvwxyz) и цифр (0-9) о — ненадо брать так как он практически не отличается от нуля… и l тоже, так как похож на большую i.
                      Модифицируй класс для капчи и будет тибе счастие :)
                        +1
                        а еще можно поверх букв кошечек и собачек нарисовать и сказать чтоб вводили тока те буквы на которых собачки:) ну или кошечки:)
                          0
                          Да, меня эта хрень на рапидшаре заколебала в свое время. Там капча «угадывалась» с 2-3 попытки.
                          0
                          Спасибо за разъяснения, но я знаю как делается капча.

                          Таким способом капчу вполне сделаешь, не сверхнадежную конечно, но вполне достаточную что бы распознать её было очень сложно. Переменный шрифт надписи + рандомное смещение + поворот на рандомный угол + переменный цвет + полупрозрачность на «сложном» фоне = вполне надежная капча.
                          0
                          Что то я вообще не понимаю, с каких пор использование GD и написание простого скрипта для работы с ним считается чем то магическим? В нете полно уроков и готовых классов для тех кто не шарит…
                            +4
                            хабр — филиал phpclasses.org
                            0
                            А как решается проблема с русскими буквами? Потому как после некоторых экспериментов выяснил, что они печатаются кракозяблями (уж не знаю почему на картинке в примере у автора все нормально в этом плане)… хотя шрифт был русский.
                              0
                              Хм, сожет с кодировкой что-то не то? Возможно вы пытайтесь сделать в вин-1251. Попробуйте преобразовать вывод в utf-8 с помощью mb_convert_encoding для строк, которые выводите.
                                +1
                                Ай, спасибо, дорогой!
                                Все получилось — как по маслу.

                                $text = mb_convert_encoding(«Testing...\r\n вторая строчка», 'UTF-8', 'CP-1251');
                                  0
                                  блин, не могу плючик поставить :(
                              0
                              В избранное!
                                +1
                                Думаю, стоит убрать var_dump(array_map('hexdec', str_split(ltrim($color, '#'), 2)));
                                  0
                                  C чего бы это? Всё правильно.
                                    0
                                    Да, действительно… Отладка.

                                    При установке цвета с помощью imagecolorallocate значения rgb должны быть 1-256. А 0 она воспинимает как отсутствие цвета. Т.е. при тестировании обнаружилось, что при установке #000000 надпись на белом фоне не выводится. Слал разбираться… А дамп забыл убрать.
                                    0
                                    При создании картинки из файла, лучше пользоваться функцией imagecreatefromstring, а не imagecreatefromjpeg и иже с ней. Будет более универсально и надежно. Потому что никто не застрахует от того, что в файле с расширением jpg на самом деле будет лежать gif картинка. И при таком раскладе функция imagecreatefromjpeg будет выдавать ошибки, так как она рассчитывает на jpeg, а ей подсовывают gif.
                                      0
                                      Возможно, надо попробывать. Но работать наверно все же подольше будет…
                                        0
                                        Я думаю в данном случае несколько сотых секунд погоды не сделают. Сам по себе процесс наложения текста довольно тяжеловесен и лимитирующим звеном по скорости тут будет он :)
                                      0
                                      Хорошая вещь.
                                      Вопрос — а что он делает с длинными строками? Разбивает на части и перносит или обрезает?
                                        0
                                        Разбивает на части. Если строка не поместилась в область, обрезает её. С длинными словами происходит тоже самое — слова длинее 24 символов разбиваются на составляющие.
                                        0
                                        а не лучше использовать ImageMagick? он побыстрее
                                          0
                                          Не везде стоит ImageMagick.
                                          На хостингах например только 40% ставят

                                        Only users with full accounts can post comments. Log in, please.