Search code examples
rubyimagemagickrmagick

Create a title with two colors in RMagick


I need to create a caption for a picture using two colors, all the words are in black except one in other color...

I've been reading the docs of RMagick I can't find a way of doing this... What I'm using right now to create the text is:

txt_name = Draw.new
image.annotate(txt_name, 0,0,0,0, "This RED has to be in red") do
    self.gravity = Magick::NorthGravity
    self.pointsize = 20
    self.fill = '#000000'
    txt_name.font_weight = 100
    self.font_family = 'Arial'
end

Any idea?? Or something to read so I can make this work??

Thanks!


Solution

  • Something like the following should work for you. (Please note that this is just to show you the process; I wouldn't recommend putting code like this into production. This is begging to be put in its own class EDIT: see below):

    draw               = Magick::Draw.new
    

    Set all of the text attributes on the draw object:

    draw.pointsize     = 20
    draw.fill          = '#000000'
    draw.gravity       = Magick::NorthGravity
    draw.font_weight   = 100
    draw.font_family   = "Arial"
    draw.font_style    = Magick::NormalStyle
    

    Get your image object (this one is just a new blank image):

    image              = Magick::Image.new(300,200)
    

    Set up the strings and measure them with get_type_metrics:

    black_text         = "This "
    red_text           = "RED"
    remainder          = " has to be in red"
    black_text_metrics = draw.get_type_metrics(black_text)
    red_text_metrics   = draw.get_type_metrics(red_text)
    remainder_metrics  = draw.get_type_metrics(remainder)
    

    Annotate with the black text:

    draw.annotate(image, 
                  black_text_metrics.width, 
                  black_text_metrics.height,
                  10,10,black_text)
    

    Change the color to red and add the red text:

    draw.fill = "#ff0000"
    draw.annotate(image,
                  red_text_metrics.width,
                  red_text_metrics.height,
                  10 + black_text_metrics.width, # x value set to the initial offset plus the width of the black text
                  10, red_text)
    

    Change the color back to black and add the remainder of the text:

    draw.fill = "#000000"
    draw.annotate(image,
                  remainder_metrics.width,
                  remainder_metrics.height,
                  10 + black_text_metrics.width + red_text_metrics.width,
                  10, remainder)
    

    Edit: This may give you an idea of how you could structure this a bit nicer:

    TextFragment = Struct.new(:string, :color)
    
    class Annotator
      attr_reader :text_fragments
      attr_accessor :current_color
    
      def initialize(color = nil)
        @current_color  = color || "#000000"
        @text_fragments = []
      end
    
      def add_string(string, color = nil)
        text_fragments << TextFragment.new(string, color || current_color)
      end
    end
    
    
    class Magick::Draw
      def annotate_multiple(image, annotator, x, y)
        annotator.text_fragments.each do |fragment|
          metrics   = get_type_metrics(fragment.string)
          self.fill = fragment.color
          annotate(image, metrics.width, metrics.height, x, y, fragment.string)
          x += metrics.width
        end
      end
    end
    

    and a usage example:

    image            = Magick::Image.new(300,200)
    draw             = Magic::Draw.new
    draw.pointsize   = 24
    draw.font_family = "Arial"
    draw.gravity     = Magick::NorthGravity
    annotator        = Annotator.new #using the default color (black)
    
    annotator.add_string "Hello "
    annotator.add_string "World", "#ff0000"
    annotator.add_string "!"
    
    draw.annotate_multiple(image, annotator, 10, 10)