scarf-0.2.6/0000755000175000017510000000000014124354725011441 5ustar rmb571rmb571scarf-0.2.6/scarf.gemspec0000644000175000017510000000346714124354725014116 0ustar rmb571rmb571######################################################### # This file has been automatically generated by gem2tgz # ######################################################### # -*- encoding: utf-8 -*- # stub: scarf 0.2.6 ruby lib Gem::Specification.new do |s| s.name = "scarf".freeze s.version = "0.2.6" s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= s.require_paths = ["lib".freeze] s.authors = ["Hari Gopal".freeze, "swdyh".freeze] s.date = "2020-02-16" s.description = "A Ruby library for generating initial avatars and identicons.".freeze s.email = "mail@harigopal.in".freeze s.files = ["lib/scarf.rb".freeze, "lib/scarf/configuration.rb".freeze, "lib/scarf/identicon.rb".freeze, "lib/scarf/image_svg.rb".freeze, "lib/scarf/initial_avatar.rb".freeze, "lib/scarf/version.rb".freeze, "test/test_helper.rb".freeze] s.homepage = "https://github.com/harigopal/scarf".freeze s.licenses = ["MIT".freeze] s.required_ruby_version = Gem::Requirement.new(">= 2.3.0".freeze) s.rubygems_version = "3.2.5".freeze s.summary = "A Ruby library for generating initial avatars and identicons.".freeze s.test_files = ["test/test_helper.rb".freeze] if s.respond_to? :specification_version then s.specification_version = 4 end if s.respond_to? :add_runtime_dependency then s.add_development_dependency(%q.freeze, ["~> 13.0"]) s.add_development_dependency(%q.freeze, ["~> 3.5"]) s.add_development_dependency(%q.freeze, ["~> 2.0"]) s.add_development_dependency(%q.freeze, ["~> 3.1"]) else s.add_dependency(%q.freeze, ["~> 13.0"]) s.add_dependency(%q.freeze, ["~> 3.5"]) s.add_dependency(%q.freeze, ["~> 2.0"]) s.add_dependency(%q.freeze, ["~> 3.1"]) end end scarf-0.2.6/lib/0000755000175000017510000000000014124354725012207 5ustar rmb571rmb571scarf-0.2.6/lib/scarf.rb0000644000175000017510000000066314124354725013637 0ustar rmb571rmb571require 'digest/sha1' require_relative 'scarf/configuration' require_relative 'scarf/image_svg' require_relative 'scarf/identicon' require_relative 'scarf/initial_avatar' # A Ruby library for generating initial avatars and identicons. module Scarf class << self attr_writer :configuration def configuration @configuration ||= Configuration.new end def configure yield(configuration) end end end scarf-0.2.6/lib/scarf/0000755000175000017510000000000014124354725013305 5ustar rmb571rmb571scarf-0.2.6/lib/scarf/identicon.rb0000644000175000017510000001065214124354725015612 0ustar rmb571rmb571module Scarf class Identicon PATCHES = [ [0, 4, 24, 20, 0], [0, 4, 20, 0], [2, 24, 20, 2], [0, 2, 22, 20, 0], [2, 14, 22, 10, 2], [0, 14, 24, 22, 0], [2, 24, 22, 13, 11, 22, 20, 2], [0, 14, 22, 0], [6, 8, 18, 16, 6], [4, 20, 10, 12, 2, 4], [0, 2, 12, 10, 0], [10, 14, 22, 10], [20, 12, 24, 20], [10, 2, 12, 10], [0, 2, 10, 0], [], ].freeze CENTER_PATCHES = [0, 4, 8, 15].freeze PATCH_SIZE = 5 attr_reader :code def initialize(str = '', opt = {}) @code = case opt[:type] when :code str.to_i when :ip Identicon.ip2code str else Identicon.calc_code str.to_s end @decode = decode @code @scale = if opt[:size] opt[:size].to_f / (PATCH_SIZE * 3) else opt[:scale] || 1 end @image_lib = ImageSVG @transparent = opt[:transparent] @patch_width = PATCH_SIZE * @scale @image = @image_lib.new(@patch_width * 3, @patch_width * 3, transparent: @transparent) @back_color = @image.color 255, 255, 255 @fore_color = opt[:color] || @image.color(@decode[:red], @decode[:green], @decode[:blue]) render end def decode(code) { center_type: (code & 0x3), center_invert: (((code >> 2) & 0x01) != 0), corner_type: ((code >> 3) & 0x0f), corner_invert: (((code >> 7) & 0x01) != 0), corner_turn: ((code >> 8) & 0x03), side_type: ((code >> 10) & 0x0f), side_invert: (((code >> 14) & 0x01) != 0), side_turn: ((code >> 15) & 0x03), red: (((code >> 16) & 0x01f) << 3), green: (((code >> 21) & 0x01f) << 3), blue: (((code >> 27) & 0x01f) << 3), } end def render center = [[1, 1]] side = [[1, 0], [2, 1], [1, 2], [0, 1]] corner = [[0, 0], [2, 0], [2, 2], [0, 2]] draw_patches(center, CENTER_PATCHES[@decode[:center_type]], 0, @decode[:center_invert]) draw_patches(side, @decode[:side_type], @decode[:side_turn], @decode[:side_invert]) draw_patches(corner, @decode[:corner_type], @decode[:corner_turn], @decode[:corner_invert]) end def draw_patches(list, patch, turn, invert) list.each do |i| draw(x: i[0], y: i[1], patch: patch, turn: turn, invert: invert) turn += 1 end end def draw(opt = {}) x = opt[:x] * @patch_width y = opt[:y] * @patch_width patch = opt[:patch] % PATCHES.size turn = opt[:turn] % 4 fore, back = if opt[:invert] [@back_color, @fore_color] else [@fore_color, @back_color] end offset = @image_lib == Scarf::ImageSVG ? 0 : 1 unless @transparent && back == @back_color @image.fill_rect( x, y, x + @patch_width - offset, y + @patch_width - offset, back ) end points = compute_points(offset, patch, turn, x, y) if @transparent if fore == @back_color @image.polygon_clip points else @image.polygon points, fore end else @image.polygon points, fore end end def write(path = "#{@code}.png") @image.write(path) end def to_blob @image.to_blob end def self.calc_code(str) extract_code(Identicon.digest(str)) end def self.ip2code(ip) code_ip = extract_code(ip.split('.')) extract_code(Identicon.digest(code_ip.to_s)) end def self.digest(str) Digest::SHA1.digest(str) end def self.extract_code(list) tmp = if list.respond_to?(:getbyte) [list.getbyte(0) << 24, list.getbyte(1) << 16, list.getbyte(2) << 8, list.getbyte(3)] else [list[0].to_i << 24, list[1].to_i << 16, list[2].to_i << 8, list[3].to_i] end tmp.inject(0) do |r, i| r | (i[31] == 1 ? -(i & 0x7fffffff) : i) end end private def compute_points(offset, patch, turn, x, y) PATCHES[patch].map do |pt| dx = pt % PATCH_SIZE dy = pt / PATCH_SIZE len = @patch_width - offset px = dx.to_f / (PATCH_SIZE - 1) * len py = dy.to_f / (PATCH_SIZE - 1) * len px, py = case turn when 1 [len - py, px] when 2 [len - px, len - py] when 3 [py, len - px] else [px, py] end [x + px, y + py] end end end end scarf-0.2.6/lib/scarf/initial_avatar.rb0000644000175000017510000000511014124354725016616 0ustar rmb571rmb571module Scarf class InitialAvatar def initialize(name, font_family: [], background_shape: nil) @name = name @font_family = font_family.length.zero? ? Scarf.configuration.font_family : font_family @background_shape = background_shape.nil? ? Scarf.configuration.background_shape : background_shape end BACKGROUND = %w(#ff4040 #7f2020 #cc5c33 #734939 #bf9c8f #995200 #4c2900 #f2a200 #ffd580 #332b1a #4c3d00 #ffee00 #b0b386 #64664d #6c8020 #c3d96c #143300 #19bf00 #53a669 #bfffd9 #40ffbf #1a332e #00b3a7 #165955 #00b8e6 #69818c #005ce6 #6086bf #000e66 #202440 #393973 #4700b3 #2b0d33 #aa86b3 #ee00ff #bf60b9 #4d3949 #ff00aa #7f0044 #f20061 #330007 #d96c7b).freeze def svg <<~WRAPPER.gsub(/$\s+/, '').strip #{background} #{initials} WRAPPER end private def background case(@background_shape) when :square return "" when :circle return "" else raise "Unknown background_shape #{@background_shape} supplied to Scarf::InitialAvatar" end end # Surround the font name with single quotes if there's a space in the name, and isn't quoted already. def quoted_fonts quoted_fonts = @font_family.map do |font| if font.include?(' ') && font[0] != "'" "'#{font}'" else font end end quoted_fonts.join(', ') end # Returns a deterministic background fill for a given name. def background_fill @background_fill ||= begin digest = "0.#{Digest::MD5.hexdigest(@name).to_i(16).to_s}".to_f index = (digest * (BACKGROUND.length - 1)).round BACKGROUND[index] end end # Returns either black or white, optimizing contrast against background color. def foreground_fill red = background_fill[1..2].to_i(16) green = background_fill[3..4].to_i(16) blue = background_fill[5..6].to_i(16) (red * 0.299 + green * 0.587 + blue * 0.114) > 186 ? '#000000' : '#FFFFFF' end # Upcase first letter of up to first two segments of the supplied name. def initials @name.split[0..1].map { |segment| segment[0] }.join('').upcase end end end scarf-0.2.6/lib/scarf/version.rb0000644000175000017510000000004514124354725015316 0ustar rmb571rmb571module Scarf VERSION = '0.2.6' end scarf-0.2.6/lib/scarf/image_svg.rb0000644000175000017510000000315214124354725015574 0ustar rmb571rmb571module Scarf class ImageSVG def initialize(width, height, opt = {}) @transparent = !!opt[:transparent] @mask = @transparent ? 'mask="clip"' : '' @masks = '' @image = '' @width = width @height = height @head = %(\n) end def color(r, g, b) 'rgb(%d, %d, %d)' % [r, g, b] end def fill_rect(x, y, _x, _y, color) @image << %(\n) end def polygon(points, color) unless points.empty? ps = points.map { |i| i.join(',') }.join(' ') @image << %(\n) end end def polygon_clip(points) unless points.empty? ps = points.map { |i| i.join(',') }.join(' ') @masks << %(\n) end end def write(path) open(path, 'wb') { |f| f.write self.to_blob } end def to_blob if @transparent @head + build_mask + @image + "\n" else @head + @image + "\n" end end private def build_mask <<~MASK #{@masks} MASK end end end scarf-0.2.6/lib/scarf/configuration.rb0000644000175000017510000000037014124354725016501 0ustar rmb571rmb571module Scarf # Store configuration for this gem. class Configuration attr_accessor :font_family attr_accessor :background_shape def initialize @font_family = %w(sans-serif) @background_shape = :circle end end end scarf-0.2.6/test/0000755000175000017510000000000014124354725012420 5ustar rmb571rmb571scarf-0.2.6/test/test_helper.rb0000644000175000017510000000013614124354725015263 0ustar rmb571rmb571require 'test/unit' require 'digest/md5' require 'fileutils' require_relative '../lib/scarf'