Search code examples
rubyimagemagickrmagick

Getting RMagick/ImageMagick gravity with text


Here is Ruby code:

require 'rmagick'

include Magick

img = Image.new(300, 300)
draw = Draw.new

draw.line(0, 150, 300, 150)
draw.line(150, 0, 150, 300)

# for each of known gravity constants...
%w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
each{|g|
  # set gravity to this value...
  draw.gravity Magick.const_get(g)
  # ...and draw text with this constant name
  draw.text 150, 150, g
}

draw.draw(img)

img.write('tmp/gravity.png')

Here is image which it produces:

For SouthEast/NorthWest and similar gravities result is as expected (text is near 150,150, moved in desired direction). But for South, North and others result is really pretty weird.

As far as I can understand from code, RMagick just translates gravity and text commands into corresponding ImageMagick drawing primitives, so, I suppose its something in ImageMagick's gravity concept that I can't get.

What is it?..


Solution

  • I suppose its something in ImageMagick's gravity concept that I can't get.

    What is it?..

    The key to understanding what's going on is to locate the CenterGravity text.

    Where is Center?

    Shifted left by 150px, and down by 150px.

    Now compare compare NorthWestGravity position.

    Where is NorthWest?

    Also translated left & down by 150px respectively. Seeing a trend?

    Your issue is with this line...

    draw.text 150, 150, g
    

    The Magick::Draw API maps to MVG spec. Use Magick::Draw.push & Magick::Draw.pop to control drawing context.

    Edit from comments...

    For setting the origin of text to be drawing, you'll need to calculate the position after evaluation the text/type metrics.

    Example.

    require 'rmagick'
    
    include Magick
    
    img = Image.new(300, 300) {
      self.background_color = "palegreen"
    }
    draw = Draw.new
    dotes = Draw.new # Dotes added for point of origin
    dotes.fill = "red"
    
    cursor = 1
    # for each of known gravity constants...
    %w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
    each{|g|
      offsetX = 150
      offsetY = cursor * 25
      dotes.circle offsetX, offsetY, offsetX+2, offsetY+2
      # Get metrics of text
      metrics = draw.get_type_metrics(img, g)
      # Full width
      if %w[NorthWestGravity WestGravity SouthWestGravity].include? g then
        offsetX -= metrics[:width]
      end
      # Full height
      if %w[SouthWestGravity SouthGravity SouthEastGravity].include? g then
        offsetY += metrics[:ascent]
      end
      # Half width
      if %w[NorthGravity SouthGravity CenterGravity].include? g then
        offsetX -= metrics[:width] / 2
      end
      # Half height
      if %w[WestGravity CenterGravity EastGravity].include? g then
        offsetY += metrics[:ascent] / 2
      end
      draw.text offsetX, offsetY, g
      cursor += 1
    }
    dotes.draw(img)
    draw.draw(img)
    
    img.write('output.png')
    

    gravity with text