class FaviconParty::Image

For handling anything related to the image data of the favicon itself

Constants

MAX_FILE_SIZE
STDEV_THRESHOLD

Threshold for stdev of color values under which the image data is considered one color

VALID_MIME_TYPES

Attributes

error[RW]
png_data[RW]
source_data[RW]

Public Class Methods

new(source_data) click to toggle source
# File lib/favicon_party/image.rb, line 30
def initialize(source_data)
  @source_data = source_data
  @png_data = nil
  @error = nil
end

Public Instance Methods

base64_png() click to toggle source
# File lib/favicon_party/image.rb, line 170
def base64_png
  Base64.encode64(to_png).split(/\s+/).join
end
base64_source_data() click to toggle source
# File lib/favicon_party/image.rb, line 166
def base64_source_data
  Base64.encode64(@source_data).split(/\s+/).join
end
blank?() click to toggle source
# File lib/favicon_party/image.rb, line 80
def blank?
  @source_data.nil? || @source_data.length <= 1
end
colors_stdev() click to toggle source
# File lib/favicon_party/image.rb, line 114
def colors_stdev
  with_temp_data_file(to_png) do |t|
    cmd = "identify -format '%[fx:image.standard_deviation]' #{t.path.to_s}"
    imagemagick_run(cmd).to_f
  end
end
dimensions() click to toggle source
# File lib/favicon_party/image.rb, line 128
def dimensions
  with_temp_data_file(@source_data) do |t|
    cmd = "convert #{t.path.to_s}[0] -format '%wx%h' info:"
    imagemagick_run(cmd)
  end
end
identify(verbose = false) click to toggle source
# File lib/favicon_party/image.rb, line 52
def identify(verbose = false)
  with_temp_data_file(@source_data) do |t|
    imagemagick_run("identify #{"-verbose" if verbose} #{t.path.to_s}")
  end
end
imagemagick_run(cmd, binmode = false) click to toggle source
# File lib/favicon_party/image.rb, line 36
def imagemagick_run(cmd, binmode = false)
  stdin, stdout, stderr, t = Open3.popen3(cmd)
  stdout.binmode if binmode
  output = stdout.read.strip
  error = stderr.read.strip
  if output.empty? && !error.nil? && !error.empty?
    raise FaviconParty::ImageMagickError.new(error)
  end
  output
end
info_str() click to toggle source
# File lib/favicon_party/image.rb, line 141
def info_str
  "#{mime_type}, #{dimensions}, #{size} bytes"
end
inspect() click to toggle source
# File lib/favicon_party/image.rb, line 174
def inspect
  %(#<FaviconParty::Image mime_type: "#{mime_type}", size: #{size}>)
end
invalid_mime_type?() click to toggle source

TODO set an option to decide how mime-type validity is handled

# File lib/favicon_party/image.rb, line 90
def invalid_mime_type?
  mime_type =~ /(text|html|x-empty|octet-stream|ERROR|zip|jar)/
end
mime_type() click to toggle source
# File lib/favicon_party/image.rb, line 47
def mime_type
  return @mime_type if defined? @mime_type
  @mime_type = get_mime_type(@source_data)
end
n_colors() click to toggle source
# File lib/favicon_party/image.rb, line 121
def n_colors
  with_temp_data_file(@source_data) do |t|
    cmd = "identify -format '%k' #{t.path.to_s}"
    imagemagick_run(cmd).to_i
  end
end
one_color?() click to toggle source
# File lib/favicon_party/image.rb, line 110
def one_color?
  n_colors == 1
end
one_pixel?() click to toggle source
# File lib/favicon_party/image.rb, line 105
def one_pixel?
  files = identify.split(/\n/)
  files.length == 1 && files[0].include?(" 1x1 ")
end
size() click to toggle source

number of bytes in the raw data

# File lib/favicon_party/image.rb, line 137
def size
  @source_data && @source_data.size || 0
end
size_too_big?() click to toggle source
# File lib/favicon_party/image.rb, line 84
def size_too_big?
  size >= MAX_FILE_SIZE
end
to_png() click to toggle source

Export source_data as a 16x16 png

# File lib/favicon_party/image.rb, line 147
def to_png
  return @png_data if !@png_data.nil?
  with_temp_data_file(@source_data) do |t|
    sizes = imagemagick_run("identify #{t.path.to_s}").split(/\n/)
    images = []
    %w(16x16 32x32 64x64).each do |dims|
      %w(32-bit 24-bit 16-bit 8-bit).each do |bd|
        images += sizes.select {|x| x.include?(dims) and x.include?(bd) }.
                       map     {|x| x.split(' ')[0] }
      end
    end
    image_to_convert = images.uniq[0] || "#{t.path.to_s}[0]"
    cmd = "convert -strip -resize 16x16! #{image_to_convert} png:fd:1"
    @png_data = imagemagick_run(cmd, true)
    raise FaviconParty::InvalidData.new("Empty png") if @png_data.empty?
    @png_data
  end
end
transparent?() click to toggle source
# File lib/favicon_party/image.rb, line 98
def transparent?
  with_temp_data_file(@source_data) do |t|
    cmd = "convert #{t.path.to_s} -format '%[opaque]' info:"
    imagemagick_run(cmd) != "true"
  end
end
valid?(options = {}) click to toggle source

Does the data look like a valid favicon?

# File lib/favicon_party/image.rb, line 60
def valid?(options = {})
  @error =
    if blank?
      "source_data is blank"
    elsif !valid_mime_type?
      "source_data mime-type is invalid - #{mime_type}"
    elsif one_pixel?
      "source_data is a 1x1 image"
    elsif size_too_big?
      "source_data file size too big"
    elsif !options[:no_color_check]
      if transparent?
        "source_data is a transparent image"
      elsif one_color?
        "png_data is one color (or close to it)"
      end
    end
  @error.nil?
end
valid_mime_type?() click to toggle source
# File lib/favicon_party/image.rb, line 94
def valid_mime_type?
  VALID_MIME_TYPES.include? mime_type
end