Class: FaviconParty::Image

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/favicon_party/image.rb

Overview

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

Constant Summary collapse

STDEV_THRESHOLD =

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

0.005
MAX_FILE_SIZE =
1024 * 1024
VALID_MIME_TYPES =
%w(
  image/x-ico
  image/x-icon
  image/png
  image/gif
  image/svg+xml
  image/jpeg
)

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#encode_utf8, #get_mime_type, #prefix_url, #with_temp_data_file

Constructor Details

#initialize(source_data) ⇒ Image

Returns a new instance of Image.



30
31
32
33
34
# File 'lib/favicon_party/image.rb', line 30

def initialize(source_data)
  @source_data = source_data
  @png_data = nil
  @error = nil
end

Instance Attribute Details

#errorObject

Returns the value of attribute error.



28
29
30
# File 'lib/favicon_party/image.rb', line 28

def error
  @error
end

#png_dataObject

Returns the value of attribute png_data.



28
29
30
# File 'lib/favicon_party/image.rb', line 28

def png_data
  @png_data
end

#source_dataObject

Returns the value of attribute source_data.



28
29
30
# File 'lib/favicon_party/image.rb', line 28

def source_data
  @source_data
end

Instance Method Details

#base64_pngObject



170
171
172
# File 'lib/favicon_party/image.rb', line 170

def base64_png
  Base64.encode64(to_png).split(/\s+/).join
end

#base64_source_dataObject



166
167
168
# File 'lib/favicon_party/image.rb', line 166

def base64_source_data
  Base64.encode64(@source_data).split(/\s+/).join
end

#blank?Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/favicon_party/image.rb', line 80

def blank?
  @source_data.nil? || @source_data.length <= 1
end

#colors_stdevObject



114
115
116
117
118
119
# 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

#dimensionsObject



128
129
130
131
132
133
# 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) ⇒ Object



52
53
54
55
56
# 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) ⇒ Object



36
37
38
39
40
41
42
43
44
45
# 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_strObject



141
142
143
# File 'lib/favicon_party/image.rb', line 141

def info_str
  "#{mime_type}, #{dimensions}, #{size} bytes"
end

#inspectObject



174
175
176
# File 'lib/favicon_party/image.rb', line 174

def inspect
  %(#<FaviconParty::Image mime_type: "#{mime_type}", size: #{size}>)
end

#invalid_mime_type?Boolean

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

Returns:

  • (Boolean)


90
91
92
# 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_typeObject



47
48
49
50
# 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_colorsObject



121
122
123
124
125
126
# 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?Boolean

Returns:

  • (Boolean)


110
111
112
# File 'lib/favicon_party/image.rb', line 110

def one_color?
  n_colors == 1
end

#one_pixel?Boolean

Returns:

  • (Boolean)


105
106
107
108
# File 'lib/favicon_party/image.rb', line 105

def one_pixel?
  files = identify.split(/\n/)
  files.length == 1 && files[0].include?(" 1x1 ")
end

#sizeObject

number of bytes in the raw data



137
138
139
# File 'lib/favicon_party/image.rb', line 137

def size
  @source_data && @source_data.size || 0
end

#size_too_big?Boolean

Returns:

  • (Boolean)


84
85
86
# File 'lib/favicon_party/image.rb', line 84

def size_too_big?
  size >= MAX_FILE_SIZE
end

#to_pngObject

Export source_data as a 16x16 png



147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# 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?Boolean

Returns:

  • (Boolean)


98
99
100
101
102
103
# 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 = {}) ⇒ Boolean

Does the data look like a valid favicon?

Returns:

  • (Boolean)


60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# 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?Boolean

Returns:

  • (Boolean)


94
95
96
# File 'lib/favicon_party/image.rb', line 94

def valid_mime_type?
  VALID_MIME_TYPES.include? mime_type
end