module Magick::Screwdrivers

Constants

VERSION

Public Class Methods

collage(files, options={}) click to toggle source
# File lib/rmagick/screwdrivers/collage.rb, line 7
def self.collage files, options={}
  options = {
    :columns       => 5,
    :scale_range   => 0.1,
    :thumb_width   => 120,
    :rotate_angle  => 20,
    :background    => 'white',
    :border        => '#DDDDDD',
    :logger        => nil
  }.merge(options)
  files = "#{files}/*" if File.directory?(files)
  imgs = ImageList.new
  imgnull = Image.new(options[:thumb_width],options[:thumb_width]) { 
    self.background_color = 'transparent' 
  }
  (options[:columns]+2).times { imgs << imgnull.dup }
  Dir.glob("#{files}") { |f|
    begin
      i = img_from_file(f)
    rescue
      warn(options[:logger], "Skipping invalid file #{f}…")
      next
    end
    scale = (1.0 + options[:scale_range]*Random::rand(-1.0..1.0))*options[:thumb_width]/[i.columns, i.rows].max
    imgs << imgnull.dup if (imgs.size % (options[:columns]+2)).zero?
    imgs << i.auto_orient.thumbnail(scale).polaroid(
      Random::rand(-options[:rotate_angle]..options[:rotate_angle])
    )
    imgs << imgnull.dup if (imgs.size % (options[:columns]+2)) == options[:columns]+1
  }
  (2*options[:columns]+4-(imgs.size % (options[:columns]+2))).times { imgs << imgnull.dup }
  info options[:logger], "Montaging image [#{options[:columns]}×#{imgs.size/(options[:columns]+2)-2}]"
  imgs.montage { 
    self.tile             = Magick::Geometry.new(options[:columns]+2) 
    self.geometry         = "-#{options[:thumb_width]/5}-#{options[:thumb_width]/4}"
    self.background_color = options[:background]
  }.trim(true).border(10,10,options[:background]).border(1,1,options[:border])
end
debug(logger = nil, msg = nil) click to toggle source
# File lib/rmagick/screwdrivers/helpers.rb, line 37
def self.debug logger = nil, msg = nil
  logger.debug(msg) if logger && logger.respond_to?(:debug)
end
img_from_file(file) click to toggle source

Image preparation =================================

# File lib/rmagick/screwdrivers/helpers.rb, line 12
def self.img_from_file file
  img = Magick::Image::read(file).first

  # case img.orientation
  # when Magick::RightTopOrientation
  #   img.rotate!(90)
  # when Magick::BottomRightOrientation
  #   img.rotate!(180)
  # when Magick::LeftBottomOrientation
  #   img.rotate!(-90)
  # end

  img
end
info(logger = nil, msg = nil) click to toggle source
# File lib/rmagick/screwdrivers/helpers.rb, line 34
def self.info logger = nil, msg = nil
  logger.info(msg) if logger && logger.respond_to?(:info)
end
poster(file, text1, text2, options={}) click to toggle source
# File lib/rmagick/screwdrivers/poster.rb, line 7
def self.poster file, text1, text2, options={}
  options = {
    :color        => '#FFFFFF',
    :stroke       => nil,
    :width        => 600,
    :type         => :classic, # :classic for black square around
    :lineheight   => 6,
    :background   => '#000000',
    :font         => '/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf',
    :max_font_size => 48,
    :logger       => nil
  }.merge(options)

  text1 ||= ''
  text2 ||= ''
  options[:lineheight] = 3 if options[:lineheight] < 3

  img = img_from_file(file)
  img.thumbnail!(options[:width].to_f/img.columns.to_f)

  mark = Magick::Image.new(img.columns, img.rows) do
    self.background_color = options[:type] == :classic ? options[:background] : 'transparent'
  end

  gc = Magick::Draw.new

  pointsize = [img.columns, options[:max_font_size]].min
  classic_margin = 0

  loop do
    gc.pointsize = pointsize -= 1 + pointsize / 33

    m1 = gc.get_type_metrics(text1)
    w1 = m1.width
    h1 = (m1.bounds.y2 - m1.bounds.y1).round

    m2 = gc.get_type_metrics(text2)
    w2 = m2.width
    h2 = (m2.bounds.y2 - m2.bounds.y1).round

    if w1 < img.columns - 10*options[:lineheight] && w2 < img.columns - 10*options[:lineheight]
      if options[:type] == :classic
        classic_margin = h2
        mark.resize! img.columns+options[:lineheight]*14/3, img.rows+options[:lineheight]*8+h1+h2
        gc.stroke_width = options[:lineheight]/3
        gc.stroke = '#FFFFFF'
        gc.fill = options[:background]
        gc.rectangle(options[:lineheight]*7/6, options[:lineheight]*7/6, \
                     img.columns+options[:lineheight]*21/6, img.rows+options[:lineheight]*21/6)
        gc.composite(
          7*options[:lineheight]/3, 7*options[:lineheight]/3,
          0, 0,
          img, Magick::OverCompositeOp
        )
        gc.draw mark
      end
      break
    end
  end

  gc.fill = options[:color]
  gc.stroke = options[:stroke] || 'none'
  gc.stroke_width = 1
  gc.font = options[:font]

  case options[:type]
  when :classic
    gc.annotate(mark, 0, 0, 0, classic_margin+2*options[:lineheight], text1) do
      self.gravity = Magick::SouthGravity
    end
  else
    gc.annotate(mark, 0, 0, 0, 0, text1) do
      self.gravity = Magick::NorthGravity
    end
  end

  gc.annotate(mark, 0, 0, 0, 0, text2) do
    self.gravity = Magick::SouthGravity
  end

  case options[:type]
  when :classic
    mark
  when :negative
    img.composite(mark, Magick::SouthEastGravity, Magick::SubtractCompositeOp)
  when :standard
    img.composite(mark, Magick::SouthEastGravity, Magick::OverCompositeOp)
  else
    warn options[:logger], "Invalid type: #{options[:type]}"
  end
end
scale(file, options={}) click to toggle source
# File lib/rmagick/screwdrivers/scale.rb, line 8
def self.scale file, options={}
  options = {
    :widths            => 600,
    :date_in_watermark => false,
    :watermark         => nil,
    :logger            => nil
  }.merge(options)

  img = img_from_file file

  date = img.get_exif_by_number(36867)[36867]
  date = Date.parse(date.gsub(/:/, '/')) if date
  date ||= Date.parse(img.properties['exif:DateTime'].gsub(/:/, '/')) if img.properties['exif:DateTime']
  date ||= Date.parse(img.properties['date:modify']) if img.properties['date:modify']
  date ||= Date.parse(img.properties['date:create']) if img.properties['date:create']
  date ||= Date.parse(img.properties['xap:CreateDate']) if img.properties['xap:CreateDate']

  options[:watermark] = ([
    options[:watermark], date.strftime('%y/%m/%d')] - [nil]
  ).join(' :: ').strip if options[:date_in_watermark]

  result = ImageList.new

  [*options[:widths]].each { |sz|
    unless Integer === sz && sz < img.columns && sz > 0
      warn options[:logger], "Invalid width #{sz} (original is #{img.columns}), skipping…"
      next
    end

    curr = img.resize_to_fit(sz)

    if will_wm = (options[:watermark] && curr.rows >= 400)
      mark = Magick::Image.new(curr.rows, curr.columns) do
        self.background_color = 'transparent'
      end
      draw = Magick::Draw.new
      draw.encoding = 'Unicode'
      draw.annotate(mark, 0, 0, 5, 2, options[:watermark]) do
        self.encoding = 'Unicode'
        self.gravity = Magick::SouthEastGravity
        self.fill 'rgba(60%, 60%, 60%, 0.40)'
        self.stroke = 'none'
        self.pointsize = 1 + 2 * Math.log(sz, 3).to_i
        self.font_family = 'Comfortaa'
        self.font_weight = Magick::NormalWeight
        self.font_style = Magick::NormalStyle
      end
      curr = curr.composite(mark.rotate(-90), Magick::SouthEastGravity, Magick::SubtractCompositeOp)
    end

    info options[:logger], "Scaling to width #{curr.rows}×#{curr.columns}, watermark: “#{will_wm ? options[:watermark] : 'NONE'}”"

    result << curr
  }

  result
end
warn(logger = nil, msg = nil) click to toggle source

Handy logging =====================================

# File lib/rmagick/screwdrivers/helpers.rb, line 31
def self.warn logger = nil, msg = nil
  logger.warn(msg) if logger && logger.respond_to?(:warn)
end