Page 1 of 1

rotate + translate + draw = problem

Posted: 2012-04-21T09:43:55-07:00
by naquad
Hi.

I want to add text with background to my thumbnails. I've came up with following solution:

Code: Select all

const MiddleGravity = 0xDEADC0DE; /// own constant, doesn't get to IMagick methods
/// IMagick constants are aliased for better readability, i.e. CenterGravity is for imagick::GRAVITY_CENTER

protected static $translations = array(
    self::WestGravity => self::SouthGravity,
    self::MiddleGravity => self::CenterGravity,
    self::EastGravity => self::NorthGravity
);
//....
public function thumbnail($options = array()){
      $options += $this->config; /// merge default options with user passed

      $options['width'] = min($options['width'], $this->image->getImageWidth()); // in case thumbnail dimensions are bigger than original image
      $options['height'] = min($options['height'], $this->image->getImageHeight());
      
      if($options['crop'])
        $this->image->cropThumbnailImage($options['width'], $options['height']);
      else
        $this->image->thumbnailImage($options['width'], $options['height']);

      if(!array_key_exists('text', $options) || empty($options['text']))
        return;

      $draw = new ImagickDraw();
      $draw->setFont($options['font']);
      $draw->setFontSize($options['size']);
      $draw->setFillColor($this->color($options['background_color'])); /// color() is just a wrapper for new ImagickPixel($color);
      $draw->setFontStyle($options['font_style']);
      $draw->setFontWeight($options['font_weight']);
      $draw->setStrokeColor($this->color('transparent'));

      /// here comes the magick. in case when user wants vertical bars we just rotate drawing context and do translation of
      /// coords then just draw corresponding horizontal bar

      if(array_key_exists($options['gravity'], self::$translations)){
        switch($options['text_direction']){ /// if user wants text to be written from top to bottom or bottom to top then we should change rotation angle
        case self::TopToBottom:
          $draw->translate($this->image->getImageHeight(), 0);
          $draw->rotate(90);
          break;
        case self::BottomToTop:
          $draw->translate(0, $this->image->getImageWidth());
          $draw->rotate(270);
          break;
        default:
          throw new ImageOperationError('Invalid text direction');
        }

        $options['gravity'] = self::$translations[$options['gravity']];

        $thumb_height = $this->image->getImageWidth(); /// after rotation width and height are swapped
        $thumb_width = $this->image->getImageHeight();
      }else{
        $thumb_width = $this->image->getImageWidth();
        $thumb_height = $this->image->getImageHeight();
      }

      $metrics = $this->image->queryFontMetrics($draw, $options['text'], false); /// get text bounding box

      $box_width = $metrics['textWidth'];
      $box_height = round($metrics['boundingBox']['y2'] - $metrics['boundingBox']['y1']); /// thats tightest box that text fits to

      if($box_width > $thumb_width - $options['padding'] * 2) /// if text is too wide
        throw new ImageOperationError('Text too long');

      $draw_text = $draw->clone(); /// save current context for text drawing

      switch($options['text_align']){ /// where to start writing text (X)
      case self::LeftAlign:
        $text_x = $options['padding'];
        break;
      case self::CenterAlign:
        $text_x = $thumb_width / 2 - $box_width / 2;
        break;
      case self::RightAlign:
        $text_x = $thumb_width - $box_width - 1- $options['padding'];
        break;
      default:
        throw new ImageOperationError('Invalid text align');
      }

      switch($options['gravity']){ /// now depending on user given gravity we should calculate where to draw background (Y) and text (Y)
      case self::NorthGravity: /// top bar
        $y1 = 0;
        $y2 = $options['padding'] * 2 + $box_height;
        $text_y = $options['padding'] + $box_height + $metrics['boundingBox']['y1'];
        break;
      case self::CenterGravity: /// on the middle
        $y1 = round($thumb_height / 2 - $box_height / 2 - $options['padding']);
        $y2 = round($thumb_height / 2 + $box_height / 2 + $options['padding']);
        $text_y = round($thumb_height / 2 + $box_height / 2 + $metrics['boundingBox']['y1']);
        break;
      case self::SouthGravity: /// bottom one
        $y1 = $thumb_height - $options['padding'] * 2 - $box_height;
        $y2 = $thumb_height;
        $text_y = $thumb_height - $options['padding'] + $metrics['boundingBox']['y1'];
        break;
      default:
        throw new ImageOperationError('Invalid gravity');
      }

      if(array_key_exists('background', $options) && $options['background']){ /// should we draw background?
        $draw->rectangle(0, $y1, $thumb_width, $y2 - 1);
        $this->image->drawImage($draw);
      }

      $draw_text->setFillColor($options['color']);
      $draw_text->annotation($text_x, $text_y, $options['text']);
      $this->image->drawImage($draw_text);
    }
(whole code: http://pastebin.com/dtXiEGFP)

When drawing horizontal bars everything is ok and works as expected, but when I'm trying to make vertical annotate method goes insane: left bar is drawn w/o couple of bottom pixels which are still available, right bar is drawn from x1 = 1-3 instead of 0.

Am I doing something wrong or its some issue of IMagick? Same code works with RMagick perfectly