Hello Everyone !!
I have released a captcha plugin Simple Captcha. It is really simple to implement, and provides a cool feature of multiple styles of images. Visit here for more explanation.
Previous post on improving image for validates_captcha
———————————————————
Validates captcha is a good plugin to implement captcha in your rails application.
However i found that there is repetition of the string of the image and the quality of image is not that good. To get a good quality image and random string
something like this

replace the code of the file /vendor/plugins/validates_captcha/lib/captcha_challenge.rb with the following code…
require 'digest/sha1'
module AngryMidgetPluginsInc #:nodoc:
# CaptchaChallenge
class CaptchaChallenge
include CaptchaConfig
extend CaptchaConfig
DEFAULT_TTL = 1200#Lifetime in seconds. Default is 20 minutes.
attr_reader :id, :created_at
attr_accessor :ttl
def initialize(options = {}) #:nodoc:
generate_id
options = {
:ttl => config['default_ttl'] || DEFAULT_TTL
}.update(options)
self.ttl = options[:ttl]
@created_at = Time.now
self.class.prune
end
# Implement in subclasses.
def correct? #:nodoc:
raise NotImplementedError
end
private
def generate_id #:nodoc:
self.id = Digest::SHA1.hexdigest(Time.now.to_s+rand.to_s)
end
def id=(i) #:nodoc:
@id = i
end
def write_to_store #:nodoc:
store.transaction{
store[:captchas] = Array.new unless store.root?(:captchas)
store[:captchas] << self
}
end
class << self
# Removes old instances from PStore
def prune
store.transaction{
if store.root?(:captchas)
store[:captchas].each_with_index{|c,i|
if Time.now > c.created_at+c.ttl
store[:captchas].delete_at(i)
end
}
end
}
end#prune
end#class << self
end
# A CAPTCHA challenge where an image with text is
# generated. A human can read the text with relative
# ease, while most robots can not. There are accessibility
# problems with this challenge, though, as people
# with reduced or no vision are unlikely to pass the test.
class CaptchaImageChallenge < CaptchaChallenge
WORDS = ‘gorilla costume, superman, banana bender, chuck norris, xray vision, ahoy me hearties,
chunky bacon, latex, rupert murdoch, clap your hands, year 2000,
sugar coated, coca cola, rastafarian, airbus a380′.split(/,s+/)
DEFAULT_DIR = ‘captcha’#public/images/captcha
WRITE_DIR = File.join(RAILS_ROOT, ‘public’, ‘images’)
DEFAULT_FILETYPE = ‘jpg’
attr_reader :image
attr_accessor :string, :dir, :filename, :filetype
# Creates an image challenge.
def initialize(options = {})
super
options = {
:string => config['words'] ? config['words'][rand(config['words'].size)] : WORDS[rand(WORDS.size)],
:dir => config['default_dir'] || DEFAULT_DIR,
:filetype => config['default_filetype'] || DEFAULT_FILETYPE
}.update(options)
self.string = Digest::SHA1.hexdigest(Time.now.to_s)[0..4].upcase #options[:string]
self.dir = options[:dir]
self.filetype = options[:filetype]
self.filename = options[:filename] || generate_filename
write_to_store
end
# Generates the image.
def generate(options = {})
options = {
:fontsize => 50,
:padding => 20,
:color => ‘#000′,
:background => ‘#fff’,
:fontweight => ‘bolw’,
:rotate => true
}.update(options)
options[:fontweight] = case options[:fontweight]
when ‘bold’ then 700
else 400
end
#added
text = Array.new
0.upto(4) do |i|
text[i] = Magick::Draw.new
text[i].pointsize = options[:fontsize]
text[i].font_weight = 600 #options[:fontweight]
text[i].fill = ‘black’ #options[:color]
text[i].gravity = Magick::CenterGravity
text[i].rotation = (rand(2)==1 ? rand(30) : -rand(30)) if options[:rotate]
end
metric = text[2].get_type_metrics(self.string)
#add bg
canvas = Magick::ImageList.new
fill = Magick::HatchFill.new(’white’,'black’)
x = metric.width+options[:padding]
y = metric.height+options[:padding]
#ADDING NOISE
img1 = Magick::Image.new(x,y,fill)
gc = Magick::Draw.new
gc.stroke_linejoin(’round’)
gc.stroke(’black’)
gc.stroke_width(1)
100.times do |i|
x1 = rand(x)
y1 = rand(y)
gc.circle(x1,y1,x1.next,y1)
end
gc.draw(img1)
canvas << img1
0.upto(4) do |i|
canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
self.background_color = ‘#000F’
y_loc = rand(y)
}.annotate(text[i], 0, 0, (i-2)*30, rand(y/4), self.string[i,1]) #.wave(5, 20)
end
canvas << Magick::Image.new(metric.width+options[:padding], metric.height+options[:padding]){
#changed
p = Magick::Pixel.from_color(options[:background])
p.opacity = Magick::MaxRGB
self.background_color = p
} #.add_noise(Magick::LaplacianNoise)
self.image = canvas.flatten_images.blur_image(1)
end
# Writes image to file.
def write(dir = self.dir, filename = self.filename)
self.image.write(File.join(WRITE_DIR, dir, filename))
end
# Determine if the supplied +string+ matches
# that used when generating the image.
def correct?(string)
string.downcase == self.string.downcase
end
# The full path to the image file, relative
# to <tt>public/images</tt>.
def file_path
File.join(dir,filename)
end
class << self
# Deletes old image files. Also calls CaptchaChallenge.prune
def prune
store.transaction{
if store.root?(:captchas)
store[:captchas].each_with_index{|c,i|
if Time.now > c.created_at+c.ttl
if File.exists?(File.join(WRITE_DIR, c.file_path))
begin
File.unlink(File.join(WRITE_DIR, c.file_path))
rescue Exception
end
end
end
}
end
}
super
end#prune
end#class << self
private
def generate_filename #:nodoc:
self.id+’.'+self.filetype
end
def image=(i) #:nodoc:
@image = i
end
end
end
visit Here to view the customized use of the plugin validates_captcha in RoR.
sur captcha, image, rails