scarf 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 4026733a01ae99c008ae69f75a4135e8cd7826a1
4
+ data.tar.gz: 1c4f9658d3e320298f623b5448b494f878928e7f
5
+ SHA512:
6
+ metadata.gz: 27ea92c4698fc7d5bc7aa44d58027b97fd55b014c156eb50e814d2228fcf2583c13a151afa38c3359c76a25325e659396e772212d2421e21097dad1938d49a48
7
+ data.tar.gz: 2dc7c53f1c897c2a31c8aecec84a0feff774951c401b9219fe1c5a5cce68c4ea0f88c13fdb5f06dba9c8a747449aa460e8f0c65643fbfdfbee0b3dacf5655f50
@@ -0,0 +1,177 @@
1
+ module Scarf
2
+ class Identicon
3
+ PATCHES = [
4
+ [0, 4, 24, 20, 0],
5
+ [0, 4, 20, 0],
6
+ [2, 24, 20, 2],
7
+ [0, 2, 22, 20, 0],
8
+ [2, 14, 22, 10, 2],
9
+ [0, 14, 24, 22, 0],
10
+ [2, 24, 22, 13, 11, 22, 20, 2],
11
+ [0, 14, 22, 0],
12
+ [6, 8, 18, 16, 6],
13
+ [4, 20, 10, 12, 2, 4],
14
+ [0, 2, 12, 10, 0],
15
+ [10, 14, 22, 10],
16
+ [20, 12, 24, 20],
17
+ [10, 2, 12, 10],
18
+ [0, 2, 10, 0],
19
+ [],
20
+ ]
21
+
22
+ CENTER_PATCHES = [0, 4, 8, 15]
23
+ PATCH_SIZE = 5
24
+
25
+ attr_reader :code
26
+
27
+ def initialize(str = '', opt = {})
28
+ case opt[:type]
29
+ when :code
30
+ @code = str.to_i
31
+ when :ip
32
+ @code = Identicon.ip2code str
33
+ else
34
+ @code = Identicon.calc_code str.to_s
35
+ end
36
+
37
+ @decode = decode @code
38
+
39
+ if opt[:size]
40
+ @scale = opt[:size].to_f / (PATCH_SIZE * 3)
41
+ else
42
+ @scale = opt[:scale] || 1
43
+ end
44
+
45
+ @image_lib = ImageSVG
46
+
47
+ @transparent = !!opt[:transparent] &&
48
+ [ImageRmagick, ImageSVG].include?(@image_lib)
49
+ @patch_width = PATCH_SIZE * @scale
50
+ @image = @image_lib.new(@patch_width * 3, @patch_width * 3,
51
+ :transparent => @transparent)
52
+ @back_color = @image.color 255, 255, 255
53
+ @fore_color = opt[:color] || @image.color(@decode[:red], @decode[:green], @decode[:blue])
54
+ render
55
+ end
56
+
57
+ def decode(code)
58
+ {
59
+ :center_type => (code & 0x3),
60
+ :center_invert => (((code >> 2) & 0x01) != 0),
61
+ :corner_type => ((code >> 3) & 0x0f),
62
+ :corner_invert => (((code >> 7) & 0x01) != 0),
63
+ :corner_turn => ((code >> 8) & 0x03),
64
+ :side_type => ((code >> 10) & 0x0f),
65
+ :side_invert => (((code >> 14) & 0x01) != 0),
66
+ :side_turn => ((code >> 15) & 0x03),
67
+ :red => (((code >> 16) & 0x01f) << 3),
68
+ :green => (((code >> 21) & 0x01f) << 3),
69
+ :blue => (((code >> 27) & 0x01f) << 3),
70
+ }
71
+ end
72
+
73
+ def render
74
+ center = [[1, 1]]
75
+ side = [[1, 0], [2, 1], [1, 2], [0, 1]]
76
+ corner = [[0, 0], [2, 0], [2, 2], [0, 2]]
77
+
78
+ draw_patches(center, CENTER_PATCHES[@decode[:center_type]],
79
+ 0, @decode[:center_invert])
80
+ draw_patches(side, @decode[:side_type],
81
+ @decode[:side_turn], @decode[:side_invert])
82
+ draw_patches(corner, @decode[:corner_type],
83
+ @decode[:corner_turn], @decode[:corner_invert])
84
+ end
85
+
86
+ def draw_patches(list, patch, turn, invert)
87
+ list.each do |i|
88
+ draw(:x => i[0], :y => i[1], :patch => patch,
89
+ :turn => turn, :invert => invert)
90
+ turn += 1
91
+ end
92
+ end
93
+
94
+ def draw(opt = {})
95
+ x = opt[:x] * @patch_width
96
+ y = opt[:y] * @patch_width
97
+ patch = opt[:patch] % PATCHES.size
98
+ turn = opt[:turn] % 4
99
+
100
+ if opt[:invert]
101
+ fore, back = @back_color, @fore_color
102
+ else
103
+ fore, back = @fore_color, @back_color
104
+ end
105
+
106
+ offset = (@image_lib == Scarf::ImageSVG) ? 0 : 1
107
+
108
+ if !(@transparent && back == @back_color)
109
+ @image.fill_rect(x, y, x + @patch_width - offset,
110
+ y + @patch_width - offset, back)
111
+ end
112
+
113
+ points = []
114
+ PATCHES[patch].each do |pt|
115
+ dx = pt % PATCH_SIZE
116
+ dy = pt / PATCH_SIZE
117
+ len = @patch_width - offset
118
+ px = dx.to_f / (PATCH_SIZE - 1) * len
119
+ py = dy.to_f / (PATCH_SIZE - 1) * len
120
+
121
+ case turn
122
+ when 1
123
+ px, py = len - py, px
124
+ when 2
125
+ px, py = len - px, len - py
126
+ when 3
127
+ px, py = py, len - px
128
+ end
129
+ points << [x + px, y + py]
130
+ end
131
+
132
+ if @transparent
133
+ if fore == @back_color
134
+ @image.polygon_clip points
135
+ else
136
+ @image.polygon points, fore
137
+ end
138
+ else
139
+ @image.polygon points, fore
140
+ end
141
+ end
142
+
143
+ def write(path = "#{@code}.png")
144
+ @image.write(path)
145
+ end
146
+
147
+ def to_blob
148
+ @image.to_blob
149
+ end
150
+
151
+ def self.calc_code(str)
152
+ extract_code(Identicon.digest(str))
153
+ end
154
+
155
+ def self.ip2code(ip)
156
+ code_ip = extract_code(ip.split('.'))
157
+ extract_code(Identicon.digest(code_ip.to_s))
158
+ end
159
+
160
+ def self.digest(str)
161
+ Digest::SHA1.digest(str)
162
+ end
163
+
164
+ def self.extract_code(list)
165
+ if list.respond_to?(:getbyte)
166
+ tmp = [list.getbyte(0) << 24, list.getbyte(1) << 16,
167
+ list.getbyte(2) << 8, list.getbyte(3)]
168
+ else
169
+ tmp = [list[0].to_i << 24, list[1].to_i << 16,
170
+ list[2].to_i << 8, list[3].to_i]
171
+ end
172
+ tmp.inject(0) do |r, i|
173
+ r | ((i[31] == 1) ? -(i & 0x7fffffff) : i)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,60 @@
1
+ module Scarf
2
+ class ImageSVG
3
+ def initialize(width, height, opt = {})
4
+ @transparent = !!opt[:transparent]
5
+ @mask = @transparent ? 'mask="clip"' : ''
6
+ @masks = ''
7
+ @image = ''
8
+ @width = width
9
+ @height = height
10
+ @head = %(<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 #{width} #{height}">\n)
11
+ end
12
+
13
+ def color(r, g, b)
14
+ 'rgb(%d, %d, %d)' % [r, g, b]
15
+ end
16
+
17
+ def fill_rect(x, y, _x, _y, color)
18
+ @image << %(<rect x="#{x}" y="#{y}" width="#{_x - x}" height="#{_y - y}" fill="#{color}" stroke-width="0" stroke="white" mask="url(#clip)"/>\n)
19
+ end
20
+
21
+ def polygon(points, color)
22
+ unless points.empty?
23
+ ps = points.map { |i| i.join(',') }.join(' ')
24
+ @image << %(<polygon points="#{ps}" stroke-width="0" fill="#{color}" mask="(#clip)"/>\n)
25
+ end
26
+ end
27
+
28
+ def polygon_clip(points)
29
+ unless points.empty?
30
+ ps = points.map { |i| i.join(',') }.join(' ')
31
+ @masks << %(<polygon points="#{ps}" stroke-width="0"/>\n)
32
+ end
33
+ end
34
+
35
+ def write(path)
36
+ open(path, 'wb') { |f| f.write self.to_blob }
37
+ end
38
+
39
+ def to_blob
40
+ if @transparent
41
+ @head + build_mask + @image + "\n</svg>"
42
+ else
43
+ @head + @image + "\n</svg>"
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ def build_mask
50
+ <<~MASK
51
+ <mask id="clip" maskUnits="userSpaceOnUse" x="0" y="0" width="#{@width}" height="#{@height}">
52
+ <g fill="black" fill-rule="evenodd">
53
+ <rect x="0" y="0" width="#{@width}" height="#{@height}" fill="white"/>
54
+ #{@masks}
55
+ </g>
56
+ </mask>
57
+ MASK
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,45 @@
1
+ module Scarf
2
+ class InitialAvatar
3
+ def initialize(name, font_family: [])
4
+ @name = name
5
+ @font_family = font_family
6
+ end
7
+
8
+ 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
9
+
10
+ def svg
11
+ <<~WRAPPER.gsub(/$\s+/, '').strip
12
+ <svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
13
+ <circle cx="50" cy="50" r="50" fill="#{fill}" />
14
+ <text fill="#FFFFFF" font-size="50" font-family="#{font_family}" x="50" y="50" text-anchor="middle" alignment-baseline="central">#{initials}</text>
15
+ </svg>
16
+ WRAPPER
17
+ end
18
+
19
+ private
20
+
21
+ # Default to sans-serif.
22
+ def font_family
23
+ (quoted_fonts + %w(sans-serif)).join(', ')
24
+ end
25
+
26
+ # Surround the font name with single quotes if there's a space in the name, and isn't quoted already.
27
+ def quoted_fonts
28
+ @font_family.map do |font|
29
+ return font unless font.include?(' ')
30
+ return font if font[0] == "'"
31
+ "'#{font}'"
32
+ end
33
+ end
34
+
35
+ # TODO: Need to add a hashing logic to ensure a supplied name always returns the same colour.
36
+ def fill
37
+ BACKGROUND.sample
38
+ end
39
+
40
+ # Upcase first letter of up to first two segments of the supplied name.
41
+ def initials
42
+ @name.split[0..1].map { |segment| segment[0] }.join('').upcase
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,3 @@
1
+ module Scarf
2
+ VERSION = '0.2.0'
3
+ end
data/lib/scarf.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'digest/sha1'
2
+ require_relative 'scarf/image_svg'
3
+ require_relative 'scarf/identicon'
4
+ require_relative 'scarf/initial_avatar'
@@ -0,0 +1,5 @@
1
+ require 'test/unit'
2
+ require 'digest/md5'
3
+ require 'fileutils'
4
+
5
+ require_relative '../lib/scarf'
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scarf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - harigopal
8
+ - swdyh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-04-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '12.0'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '12.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: test-unit
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '3.1'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '3.1'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.5'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.5'
56
+ description: |-
57
+ A Ruby library for generating initial avatars and identicons, forked from swdyh/quilt
58
+ https://github.com/harigopal/quilt
59
+ email: mail@harigopal.in
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - lib/scarf.rb
65
+ - lib/scarf/identicon.rb
66
+ - lib/scarf/image_svg.rb
67
+ - lib/scarf/initial_avatar.rb
68
+ - lib/scarf/version.rb
69
+ - test/test_helper.rb
70
+ homepage: https://github.com/harigopal/scarf
71
+ licenses:
72
+ - MIT
73
+ metadata: {}
74
+ post_install_message:
75
+ rdoc_options: []
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 2.3.0
83
+ required_rubygems_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - ">="
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 2.6.11
91
+ signing_key:
92
+ specification_version: 4
93
+ summary: A Ruby library for generating initial avatars and identicons.
94
+ test_files:
95
+ - test/test_helper.rb