123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182 |
- <?php
- class Image_Text
- {
-
- const IMAGE_TEXT_REGEX_HTMLCOLOR
- = "/^[#|]([a-f0-9]{2})?([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i";
-
- const IMAGE_TEXT_ALIGN_LEFT = "left";
-
- const IMAGE_TEXT_ALIGN_RIGHT = "right";
-
- const IMAGE_TEXT_ALIGN_CENTER = "center";
-
- const IMAGE_TEXT_ALIGN_TOP = "top";
-
- const IMAGE_TEXT_ALIGN_MIDDLE = "middle";
-
- const IMAGE_TEXT_ALIGN_BOTTOM = "bottom";
-
- const IMAGE_TEXT_ALIGN_JUSTIFY = "justify";
-
- private $_options = array(
-
- 'x' => 0,
- 'y' => 0,
-
- 'canvas' => null,
- 'antialias' => true,
-
- 'width' => 0,
- 'height' => 0,
-
- 'halign' => self::IMAGE_TEXT_ALIGN_LEFT,
- 'valign' => self::IMAGE_TEXT_ALIGN_TOP,
-
- 'angle' => 0,
-
- 'color' => array('#000000'),
- 'color_mode' => 'line',
- 'background_color' => '#000000',
- 'enable_alpha' => false,
-
- 'font_path' => "./",
- 'font_file' => null,
- 'font_size' => 2,
- 'line_spacing' => 0.5,
-
- 'min_font_size' => 1,
- 'max_font_size' => 100,
-
- 'max_lines' => 100,
-
- 'image_type' => IMAGETYPE_PNG,
- 'dest_file' => ''
- );
-
- private $_reInits = array(
- 'width', 'height', 'canvas', 'angle', 'font_file', 'font_path', 'font_size'
- );
-
- private $_text;
-
- private $_img;
-
- private $_tokens = array();
-
- private $_font;
-
- private $_bbox = array();
-
- private $_mode = '';
-
- private $_colors = array();
-
- private $_realTextSize = array('width' => false, 'height' => false);
-
- private $_lines = false;
-
- private $_measurizedSize = false;
-
- private $_init = false;
-
- public function __construct($text, $options = null)
- {
- $this->set('text', $text);
- if (!empty($options)) {
- $this->_options = array_merge($this->_options, $options);
- }
- }
-
- public static function construct($text, $options)
- {
- $itext = new Image_Text($text, $options);
- $itext->init();
- return $itext;
- }
-
- public function set($option, $value = null)
- {
- $reInits = array_flip($this->_reInits);
- if (!is_array($option)) {
- if (!isset($value)) {
- throw new Image_Text_Exception('No value given.');
- }
- $option = array($option => $value);
- }
- foreach ($option as $opt => $val) {
- switch ($opt) {
- case 'color':
- $this->setColors($val);
- break;
- case 'text':
- if (is_array($val)) {
- $this->_text = implode('\n', $val);
- } else {
- $this->_text = $val;
- }
- break;
- default:
- $this->_options[$opt] = $val;
- break;
- }
- if (isset($reInits[$opt])) {
- $this->_init = false;
- }
- }
- }
-
- public function setColors($colors)
- {
- $i = 0;
- if (is_array($colors) && (is_string($colors[0]) || is_array($colors[0]))) {
- foreach ($colors as $color) {
- $this->setColor($color, $i++);
- }
- } else {
- $this->setColor($colors, $i);
- }
- }
-
- function setColor($color, $id = 0)
- {
- if (is_array($color)) {
- if (isset($color['r']) && isset($color['g']) && isset($color['b'])) {
- $color['a'] = isset($color['a']) ? $color['a'] : 0;
- $this->_options['colors'][$id] = $color;
- } else if (isset($color[0]) && isset($color[1]) && isset($color[2])) {
- $color['r'] = $color[0];
- $color['g'] = $color[1];
- $color['b'] = $color[2];
- $color['a'] = isset($color[3]) ? $color[3] : 0;
- $this->_options['colors'][$id] = $color;
- } else {
- throw new Image_Text_Exception(
- 'Use keys 1,2,3 (optionally) 4 or r,g,b and (optionally) a.'
- );
- }
- } elseif (is_string($color)) {
- $color = $this->convertString2RGB($color);
- if ($color) {
- $this->_options['color'][$id] = $color;
- } else {
- throw new Image_Text_Exception('Invalid color.');
- }
- }
- if ($this->_img) {
- $aaFactor = ($this->_options['antialias']) ? 1 : -1;
- if (function_exists('imagecolorallocatealpha') && isset($color['a'])) {
- $this->_colors[$id] = $aaFactor *
- imagecolorallocatealpha(
- $this->_img,
- $color['r'], $color['g'], $color['b'], $color['a']
- );
- } else {
- $this->_colors[$id] = $aaFactor *
- imagecolorallocate(
- $this->_img,
- $color['r'], $color['g'], $color['b']
- );
- }
- if ($this->_colors[$id] == 0 && $aaFactor == -1) {
-
-
- $this->_colors[$id] = -256;
- }
- }
- }
-
- public function init()
- {
-
- $fontFile = rtrim($this->_options['font_path'], '/\\');
- $fontFile .= defined('OS_WINDOWS') && OS_WINDOWS ? '\\' : '/';
- $fontFile .= $this->_options['font_file'];
- $fontFile = realpath($fontFile);
- if (empty($fontFile)) {
- throw new Image_Text_Exception('You must supply a font file.');
- } elseif (!file_exists($fontFile)) {
- throw new Image_Text_Exception('Font file was not found.');
- } elseif (!is_readable($fontFile)) {
- throw new Image_Text_Exception('Font file is not readable.');
- } elseif (!imagettfbbox(1, 1, $fontFile, 1)) {
- throw new Image_Text_Exception('Font file is not valid.');
- }
- $this->_font = $fontFile;
-
- if ($this->_options['width'] < 1) {
- throw new Image_Text_Exception('Width too small. Has to be > 1.');
- }
-
- $image_canvas = false;
- switch (true) {
- case (empty($this->_options['canvas'])):
-
- $this->_img = imagecreatetruecolor(
- $this->_options['width'], $this->_options['height']
- );
- if (!$this->_img) {
- throw new Image_Text_Exception('Could not create image canvas.');
- }
- break;
- case (is_resource($this->_options['canvas']) &&
- get_resource_type($this->_options['canvas']) == 'gd'):
-
- $image_canvas = true;
- $this->_img = $this->_options['canvas'];
- break;
- case (is_array($this->_options['canvas']) &&
- isset($this->_options['canvas']['width']) &&
- isset($this->_options['canvas']['height'])):
-
- $this->_img = imagecreatetruecolor(
- $this->_options['canvas']['width'],
- $this->_options['canvas']['height']
- );
- break;
- case (is_array($this->_options['canvas']) &&
- isset($this->_options['canvas']['size']) &&
- ($this->_options['canvas']['size'] = 'auto')):
- case (is_string($this->_options['canvas']) &&
- ($this->_options['canvas'] = 'auto')):
- $this->_mode = 'auto';
- break;
- default:
- throw new Image_Text_Exception('Could not create image canvas.');
- }
- if ($this->_img) {
- $this->_options['canvas'] = array();
- $this->_options['canvas']['width'] = imagesx($this->_img);
- $this->_options['canvas']['height'] = imagesy($this->_img);
- }
- if ($this->_options['enable_alpha']) {
- imagesavealpha($this->_img, true);
- imagealphablending($this->_img, false);
- }
- if ($this->_options['background_color'] === null) {
- $this->_options['enable_alpha'] = true;
- imagesavealpha($this->_img, true);
- imagealphablending($this->_img, false);
- $colBg = imagecolorallocatealpha($this->_img, 255, 255, 255, 127);
- } else {
- $arBg = $this->convertString2RGB($this->_options['background_color']);
- if ($arBg === false) {
- throw new Image_Text_Exception('Background color is invalid.');
- }
- $colBg = imagecolorallocatealpha(
- $this->_img, $arBg['r'], $arBg['g'], $arBg['b'], $arBg['a']
- );
- }
- if ($image_canvas === false) {
- imagefilledrectangle(
- $this->_img,
- 0, 0,
- $this->_options['canvas']['width'] - 1,
- $this->_options['canvas']['height'] - 1,
- $colBg
- );
- }
-
- $angle = $this->_options['angle'];
- while ($angle < 0) {
- $angle += 360;
- }
- if ($angle > 359) {
- $angle = $angle % 360;
- }
- $this->_options['angle'] = $angle;
-
- $this->setColors($this->_options['color']);
- $this->_lines = null;
-
- $this->_init = true;
- }
-
- public function autoMeasurize($start = false, $end = false)
- {
- if (!$this->_init) {
- throw new Image_Text_Exception('Not initialized. Call ->init() first!');
- }
- $start = (empty($start)) ? $this->_options['min_font_size'] : $start;
- $end = (empty($end)) ? $this->_options['max_font_size'] : $end;
-
-
- for ($i = $start; $i <= $end; $i++) {
- $this->_options['font_size'] = $i;
- $res = $this->measurize();
- if ($res === false) {
- if ($start == $i) {
- $this->_options['font_size'] = -1;
- throw new Image_Text_Exception("No possible font size found");
- }
- $this->_options['font_size'] -= 1;
- $this->_measurizedSize = $this->_options['font_size'];
- break;
- }
-
- $this->_lines = $res;
- }
- return $this->_options['font_size'];
- }
-
- public function measurize($force = false)
- {
- if (!$this->_init) {
- throw new Image_Text_Exception('Not initialized. Call ->init() first!');
- }
- $this->_processText();
-
- $font = $this->_font;
- $size = $this->_options['font_size'];
- $space = (1 + $this->_options['line_spacing'])
- * $this->_options['font_size'];
- $max_lines = (int)$this->_options['max_lines'];
- if (($max_lines < 1) && !$force) {
- return false;
- }
- $block_width = $this->_options['width'];
- $block_height = $this->_options['height'];
- $colors_cnt = sizeof($this->_colors);
- $text_line = '';
- $lines_cnt = 0;
- $lines = array();
- $text_height = 0;
- $text_width = 0;
- $i = 0;
- $para_cnt = 0;
- $width = 0;
- $beginning_of_line = true;
-
- foreach ($this->_tokens as $token) {
-
- if ($token == "\n") {
- $bounds = imagettfbbox($size, 0, $font, $text_line);
- if ((++$lines_cnt >= $max_lines) && !$force) {
- return false;
- }
- if ($this->_options['color_mode'] == 'paragraph') {
- $c = $this->_colors[$para_cnt % $colors_cnt];
- $i++;
- } else {
- $c = $this->_colors[$i++ % $colors_cnt];
- }
- $lines[] = array(
- 'string' => $text_line,
- 'width' => $bounds[2] - $bounds[0],
- 'height' => $bounds[1] - $bounds[7],
- 'bottom_margin' => $bounds[1],
- 'left_margin' => $bounds[0],
- 'color' => $c
- );
- $text_width = max($text_width, ($bounds[2] - $bounds[0]));
- $text_height += (int)$space;
- if (($text_height > $block_height) && !$force) {
- return false;
- }
- $para_cnt++;
- $text_line = '';
- $beginning_of_line = true;
- continue;
- }
-
- if ($beginning_of_line) {
- $text_line = '';
- $text_line_next = $token;
- $beginning_of_line = false;
- } else {
- $text_line_next = $text_line . ' ' . $token;
- }
- $bounds = imagettfbbox($size, 0, $font, $text_line_next);
- $prev_width = isset($prev_width) ? $width : 0;
- $width = $bounds[2] - $bounds[0];
-
- if ($width > $block_width) {
- if ((++$lines_cnt >= $max_lines) && !$force) {
- return false;
- }
- if ($this->_options['color_mode'] == 'line') {
- $c = $this->_colors[$i++ % $colors_cnt];
- } else {
- $c = $this->_colors[$para_cnt % $colors_cnt];
- $i++;
- }
- $lines[] = array(
- 'string' => $text_line,
- 'width' => $prev_width,
- 'height' => $bounds[1] - $bounds[7],
- 'bottom_margin' => $bounds[1],
- 'left_margin' => $bounds[0],
- 'color' => $c
- );
- $text_width = max($text_width, ($bounds[2] - $bounds[0]));
- $text_height += (int)$space;
- if (($text_height > $block_height) && !$force) {
- return false;
- }
- $text_line = $token;
- $bounds = imagettfbbox($size, 0, $font, $text_line);
- $width = $bounds[2] - $bounds[0];
- $beginning_of_line = false;
- } else {
- $text_line = $text_line_next;
- }
- }
-
- $bounds = imagettfbbox($size, 0, $font, $text_line);
- $i++;
- if ($this->_options['color_mode'] == 'line') {
- $c = $this->_colors[$i % $colors_cnt];
- } else {
- $c = $this->_colors[$para_cnt % $colors_cnt];
- }
- $lines[] = array(
- 'string' => $text_line,
- 'width' => $bounds[2] - $bounds[0],
- 'height' => $bounds[1] - $bounds[7],
- 'bottom_margin' => $bounds[1],
- 'left_margin' => $bounds[0],
- 'color' => $c
- );
-
- $text_height += $this->_options['font_size'];
- $text_width = max($text_width, ($bounds[2] - $bounds[0]));
- if (($text_height > $block_height) && !$force) {
- return false;
- }
- $this->_realTextSize = array(
- 'width' => $text_width, 'height' => $text_height
- );
- $this->_measurizedSize = $this->_options['font_size'];
- return $lines;
- }
-
- public function render($force = false)
- {
- if (!$this->_init) {
- throw new Image_Text_Exception('Not initialized. Call ->init() first!');
- }
- if (!$this->_tokens) {
- $this->_processText();
- }
- if (empty($this->_lines)
- || ($this->_measurizedSize != $this->_options['font_size'])
- ) {
- $this->_lines = $this->measurize($force);
- }
- $lines = $this->_lines;
- if ($this->_mode === 'auto') {
- $this->_img = imagecreatetruecolor(
- $this->_realTextSize['width'],
- $this->_realTextSize['height']
- );
- if (!$this->_img) {
- throw new Image_Text_Exception('Could not create image canvas.');
- }
- $this->_mode = '';
- $this->setColors($this->_options['color']);
- }
- $block_width = $this->_options['width'];
- $max_lines = $this->_options['max_lines'];
- $angle = $this->_options['angle'];
- $radians = round(deg2rad($angle), 3);
- $font = $this->_font;
- $size = $this->_options['font_size'];
- $line_spacing = $this->_options['line_spacing'];
- $align = $this->_options['halign'];
- $offset = $this->_getOffset();
- $start_x = $offset['x'];
- $start_y = $offset['y'];
- $sinR = sin($radians);
- $cosR = cos($radians);
- switch ($this->_options['valign']) {
- case self::IMAGE_TEXT_ALIGN_TOP:
- $valign_space = 0;
- break;
- case self::IMAGE_TEXT_ALIGN_MIDDLE:
- $valign_space = ($this->_options['height']
- - $this->_realTextSize['height']) / 2;
- break;
- case self::IMAGE_TEXT_ALIGN_BOTTOM:
- $valign_space = $this->_options['height']
- - $this->_realTextSize['height'];
- break;
- default:
- $valign_space = 0;
- }
- $space = (1 + $line_spacing) * $size;
-
-
- $new_posx = $start_x + ($sinR * ($valign_space + $size));
- $new_posy = $start_y + ($cosR * ($valign_space + $size));
- $lines_cnt = min($max_lines, sizeof($lines));
- $bboxes = array();
-
- for ($i = 0; $i < $lines_cnt; $i++) {
-
-
- if ($i > 0) {
- $new_posx += $sinR * $space;
- $new_posy += $cosR * $space;
- }
-
-
- $left_margin = $lines[$i]['left_margin'];
- $line_width = $lines[$i]['width'];
-
-
- switch ($align) {
- case self::IMAGE_TEXT_ALIGN_LEFT:
- $hyp = 0;
- break;
- case self::IMAGE_TEXT_ALIGN_RIGHT:
- $hyp = $block_width - $line_width - $left_margin;
- break;
- case self::IMAGE_TEXT_ALIGN_CENTER:
- $hyp = ($block_width - $line_width) / 2 - $left_margin;
- break;
- default:
- $hyp = 0;
- break;
- }
- $posx = $new_posx + $cosR * $hyp;
- $posy = $new_posy - $sinR * $hyp;
- $c = $lines[$i]['color'];
-
- $bboxes[] = imagettftext(
- $this->_img, $size, $angle, $posx, $posy,
- $c, $font, $lines[$i]['string']
- );
- }
- $this->_bbox = $bboxes;
- }
-
- public function getImg()
- {
- return $this->_img;
- }
-
- public function display($save = false, $free = false)
- {
- if (!headers_sent()) {
- header(
- "Content-type: " .
- image_type_to_mime_type($this->_options['image_type'])
- );
- } else {
- throw new Image_Text_Exception('Header already sent.');
- }
- switch ($this->_options['image_type']) {
- case IMAGETYPE_PNG:
- $imgout = 'imagepng';
- break;
- case IMAGETYPE_JPEG:
- $imgout = 'imagejpeg';
- break;
- case IMAGETYPE_BMP:
- $imgout = 'imagebmp';
- break;
- default:
- throw new Image_Text_Exception('Unsupported image type.');
- }
- if ($save) {
- $imgout($this->_img);
- $this->save();
- } else {
- $imgout($this->_img);
- }
- if ($free) {
- $res = imagedestroy($this->_img);
- if (!$res) {
- throw new Image_Text_Exception('Destroying image failed.');
- }
- }
- }
-
- public function save($destFile = false)
- {
- if (!$destFile) {
- $destFile = $this->_options['dest_file'];
- }
- if (!$destFile) {
- throw new Image_Text_Exception("Invalid desitination file.");
- }
- switch ($this->_options['image_type']) {
- case IMAGETYPE_PNG:
- $imgout = 'imagepng';
- break;
- case IMAGETYPE_JPEG:
- $imgout = 'imagejpeg';
- break;
- case IMAGETYPE_BMP:
- $imgout = 'imagebmp';
- break;
- default:
- throw new Image_Text_Exception('Unsupported image type.');
- break;
- }
- $res = $imgout($this->_img, $destFile);
- if (!$res) {
- throw new Image_Text_Exception('Saving file failed.');
- }
- }
-
- private function _getOffset()
- {
-
- $width = $this->_options['width'];
- $height = $this->_options['height'];
- $angle = $this->_options['angle'];
- $x = $this->_options['x'];
- $y = $this->_options['y'];
-
- if (!empty($this->_options['cx']) && !empty($this->_options['cy'])) {
- $cx = $this->_options['cx'];
- $cy = $this->_options['cy'];
-
- $x = $cx - ($width / 2);
- $y = $cy - ($height / 2);
-
- if ($angle) {
- $ang = deg2rad($angle);
-
- $vA = array(($cx - $x), ($cy - $y));
-
-
- $sin = round(sin($ang), 14);
- $cos = round(cos($ang), 14);
-
- $mRot = array(
- $cos, (-$sin),
- $sin, $cos
- );
-
-
- $vB = array(
- ($mRot[0] * $vA[0] + $mRot[2] * $vA[0]),
- ($mRot[1] * $vA[1] + $mRot[3] * $vA[1])
- );
-
- $vC = array(
- ($vA[0] - $vB[0]),
- ($vA[1] - $vB[1])
- );
-
- $x += $vC[0];
- $y += $vC[1];
- }
- }
- return array('x' => (int)round($x, 0), 'y' => (int)round($y, 0));
- }
-
- public static function convertString2RGB($scolor)
- {
- if (preg_match(self::IMAGE_TEXT_REGEX_HTMLCOLOR, $scolor, $matches)) {
- return array(
- 'r' => hexdec($matches[2]),
- 'g' => hexdec($matches[3]),
- 'b' => hexdec($matches[4]),
- 'a' => hexdec(!empty($matches[1]) ? $matches[1] : 0),
- );
- }
- return false;
- }
-
- private function _processText()
- {
- if (!isset($this->_text)) {
- return;
- }
- $this->_tokens = array();
-
- $this->_text = preg_replace("[\r\n]", "\n", $this->_text);
-
- $paras = explode("\n", $this->_text);
-
-
- foreach ($paras as $para) {
- $words = explode(' ', $para);
- foreach ($words as $word) {
- $this->_tokens[] = $word;
- }
-
- $this->_tokens[] = "\n";
- }
-
- array_pop($this->_tokens);
- }
- }
|