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



168
169
170
# File 'lib/favicon_party/image.rb', line 168

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

#base64_source_dataObject



164
165
166
# File 'lib/favicon_party/image.rb', line 164

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

#blank?Boolean

Returns:

  • (Boolean)


78
79
80
# File 'lib/favicon_party/image.rb', line 78

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

#colors_stdevObject



112
113
114
115
116
117
# File 'lib/favicon_party/image.rb', line 112

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



126
127
128
129
130
131
# File 'lib/favicon_party/image.rb', line 126

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



139
140
141
# File 'lib/favicon_party/image.rb', line 139

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

#inspectObject



172
173
174
# File 'lib/favicon_party/image.rb', line 172

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)


88
89
90
# File 'lib/favicon_party/image.rb', line 88

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



119
120
121
122
123
124
# File 'lib/favicon_party/image.rb', line 119

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)


108
109
110
# File 'lib/favicon_party/image.rb', line 108

def one_color?
  colors_stdev < STDEV_THRESHOLD
end

#one_pixel?Boolean

Returns:

  • (Boolean)


103
104
105
106
# File 'lib/favicon_party/image.rb', line 103

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

#sizeObject

number of bytes in the raw data



135
136
137
# File 'lib/favicon_party/image.rb', line 135

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

#size_too_big?Boolean

Returns:

  • (Boolean)


82
83
84
# File 'lib/favicon_party/image.rb', line 82

def size_too_big?
  size >= MAX_FILE_SIZE
end

#to_pngObject

Export source_data as a 16x16 png



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/favicon_party/image.rb', line 145

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)


96
97
98
99
100
101
# File 'lib/favicon_party/image.rb', line 96

def transparent?
  with_temp_data_file(@source_data) do |t|
    cmd = "convert #{t.path.to_s} -channel a -negate -format '%[mean]' info:"
    imagemagick_run(cmd).to_i == 0
  end
end

#valid?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
# File 'lib/favicon_party/image.rb', line 60

def valid?
  @error =
    if blank?
      "source_data is blank"
    elsif size_too_big?
      "source_data file size too big"
    elsif !valid_mime_type?
      "source_data mime-type is invalid - #{mime_type}"
    elsif transparent?
      "source_data is a transparent image"
    elsif one_pixel?
      "source_data is a 1x1 image"
    elsif one_color?
      "png_data is one color (or close to it)"
    end
  @error.nil?
end

#valid_mime_type?Boolean

Returns:

  • (Boolean)


92
93
94
# File 'lib/favicon_party/image.rb', line 92

def valid_mime_type?
  VALID_MIME_TYPES.include? mime_type
end