We are a software consultancy based in Berlin, Germany. We deliver
high quality web apps in short timespans.

Upstream Agile GmbH

Text-im-Bild mit Rails, file_column und RMagick Teil 2/2

June 16, 2007 by alex

Im ersten Teil habe ich gezeigt, wie man mit file_column in Rails automatisch andere Bilder zu einem Bild hinzufügt, um z.B. Rahmen oder Hintergründe hinzuzufügen.

In diesem Teil geht es darum, auch Text in ein Bild schreiben zu können: Hierbei ergeben sich zwei Probleme. Zum einen muss der Text vom Model in die file_column-Klassen gelangen, zum anderen muss er dort via RMagick in das Bild gerendert werden. Das Ergebnis könnte dann z.B. so aussehen (autoki-Quartettkarte):

BMW 1er

Fangen wir von hinten an, RMagick.

Text in RMagick rendern

Die Beschreibung des Texts im Model soll folgendermaßen aussehen:

file_column :photo, :magick => { :versions =>
  {
    :badge => {:name => "badge", :text =>
      {
        :text => 'hello world',
        : x => 10,
        :y => 241,
        :color => '#363636',
        :size => 9
      }
	}
}}

Hierbei soll an die Koordinaten 10/241 der text hello world in der Schriftgröße 9 und in Dunkelgrau gerendert werden.

Um den Text in unser Bild rendern zu können, erweitern wir wieder die transform_image-Methode in der Datei magick_file_column.rb des file_column-Plugins:

if img_options[:composite]
...
end
if img_options[:text]
  if img_options[:text].is_a?(Array)
    img_options[:text].each do |text_options|
      img = add_text text_options, img
    end
  else
    img = add_text img_options[:text], img
  end
end

def add_text(text_options, img)
  text = text_options[:text]
  unless text.blank?
    img = img.annotate ::Magick::Draw.new, 0, 0, text_options[: x], text_options[:y], text do
      self.pointsize = text_options[:size] || 11
      self.font_weight = text_options[:weight] || 400
      self.fill = text_options[:color] || 'black'
    end
  end
  img
end

Das war’s eigentlich auch schon - jetzt haben wir ein hello world in unserem Bild. Teil 2…

Werte aus dem Model auf's Bild

Neben statischem text wollen wir auch dynamische Inhalte in unsere Bilder rendern. Wie auf der Autokarte oben zu sehen, rendern wir hier den Namen und die Leistungsdaten des Autos, die bei autoki aus der Datenbank kommen.

Leider gibt es dabei ein kleines Problem: Die Definition der file_column-Bildgrößen hängen nicht an der Model-Instanz, sondern an der Model-Klasse, d.h. an der Stelle, an der wir die Texte definieren, stehen die Daten aus der Datenbank noch gar nicht zur Verfügung. Folgendes funktioniert also nicht.

file_column :photo, :magick => { :versions =>
  {
    :badge => {:name => "badge", :text =>
      {
        :text => "#{self.top_speed} km/h",
        :x => 10,
        :y => 241,
      }
	}
}}

Stattdessen müssen wir einen kleinen Umweg gehen, wir verwenden Procs, die erst zur Laufzeit der Anwendung den Text generieren:

file_column :photo, :magick => { :versions =>
  {
    :badge => {:name => "badge", :text =>
      {
        :text => Proc.new {|model| "#{model.top_speed} km/h"},
        :x => 10,
        :y => 241,
      }
	}
}}

Damit das ganze funktioniert, muss file_column noch etwas erweitert werden. Zum einen muss die add_text-Methode die Procs aufrufen:

def add_text(text_options, img)
  text = text_options[:text]
  text = text.call(@object) if text.is_a?(Proc)
  unless text.blank?
    img = img.annotate ::Magick::Draw.new, 0, 0, text_options[:x], text_options[:y], text do
      self.pointsize = text_options[:size] || 11
      self.font_weight = text_options[:weight] || 400
      self.fill = text_options[:color] || 'black'
    end
  end
  img
end

Zum anderen muss die Model-Instanz file_column zur Verfügung gestellt werden. Dazu erweitern wir die Methoden transform_with_magick und create_magick_version_if_needed um eine Parameter object

def transform_with_magick(object)
  @object = object
  ..
end

def create_magick_version_if_needed(version, object)
  @object = object
  ..
end

Zuletzt muss noch der Aufruf von create_magick_version_if_needed in der Methode url_for_image_column in der Datei file_column_helper.rb angepasst werden:

def url_for_image_column(object, method, options=nil)
  ..
  if options
    subdir = object.send("#{method}_state").create_magick_version_if_needed(options, object)
  end
  ..
end

Das war’s. Wenn ich nichts vergessen habe, gibt’s jetzt dynamische Textinhalte in Bildern mit file_column. In einem nächsten Teil dann: Bilderkompression, Amazon S3 Backups.

Wie sehen uns beim Autoquartett.

P.S. Am 5.7. werde ich das ganze bei der Berlin Ruby User Group noch einmal live zeigen, kommt vorbei.

P.P.S. ich schreibe das hier gerade mit der neuen Beta von Safari 3. Vom User vergrößerbare Textareas - Gold wert mit Wordpress.