default-avatar-generator 0.1.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 +7 -0
- data/CHANGELOG.md +13 -0
- data/README.md +52 -0
- data/lib/assets/backgrounds/gradient-2h.svg +19 -0
- data/lib/assets/backgrounds/gradient-3h.svg +24 -0
- data/lib/assets/backgrounds/gradient-backslash.svg +12 -0
- data/lib/assets/backgrounds/gradient-lr.svg +12 -0
- data/lib/assets/backgrounds/gradient-slash.svg +12 -0
- data/lib/assets/backgrounds/gradient-tb.svg +12 -0
- data/lib/assets/backgrounds/solid.svg +3 -0
- data/lib/assets/foregrounds/head1-hollow.svg +7 -0
- data/lib/assets/foregrounds/head1-solid.svg +7 -0
- data/lib/assets/foregrounds/head2.svg +7 -0
- data/lib/assets/foregrounds/head3.svg +9 -0
- data/lib/assets/text/base.svg +4 -0
- data/lib/assets/text/hack.svg +4 -0
- data/lib/default_avatar_generator/background_layer.rb +12 -0
- data/lib/default_avatar_generator/colors.rb +346 -0
- data/lib/default_avatar_generator/composer.rb +18 -0
- data/lib/default_avatar_generator/foreground_layer.rb +16 -0
- data/lib/default_avatar_generator/generator.rb +93 -0
- data/lib/default_avatar_generator/image_converter.rb +27 -0
- data/lib/default_avatar_generator/layer.rb +44 -0
- data/lib/default_avatar_generator/svg_utils.rb +12 -0
- data/lib/default_avatar_generator/text_layer.rb +12 -0
- data/lib/default_avatar_generator/version.rb +5 -0
- data/lib/default_avatar_generator.rb +17 -0
- metadata +98 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 74971c4d340f209e3c83dc104158b023f74ce58c47bbf01c99b4f716bb2b75d0
|
4
|
+
data.tar.gz: 34d505f98d67c5d944001bd5a0d7de6b78bf9dd75226088cc9e53416c0678890
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: '03832b376aad000e209eafdd17c114aa94672e8916013375ac4eff993eb0652a8b1d4646e3dcea81deece7b802ac3f233dad14c010fe939164d109fdfe69b1d0'
|
7
|
+
data.tar.gz: f3203f2a6adc655f5a8ec8e736aafa586e10550e0082fbee9ce1c269fc4a8bce666d61d94003be78c9f41f80883429c04cdf491d4e4dd00ca22d83d2d96bb637
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [0.1.0] - 2024-04-08
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Initial project setup as a ruby gem
|
13
|
+
- Generates SVG and JPEG images based on the included SVG files
|
data/README.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Default Avatar Generator
|
2
|
+
|
3
|
+
[](https://github.com/pas256/default-avatar-generator/actions/workflows/ci.yml)
|
4
|
+
|
5
|
+
A Ruby gem that generates beautiful default avatars for user accounts when no custom avatar is provided.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'default-avatar-generator'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle install
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
$ gem install default-avatar-generator
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require 'default_avatar_generator'
|
27
|
+
|
28
|
+
# Generate an avatar for a user
|
29
|
+
generator = DefaultAvatarGenerator::Generator.new(text: { character: "A" })
|
30
|
+
svg_avatar = generator.generate
|
31
|
+
jpeg_avatar = DefaultAvatarGenerator::ImageConverter.svg_to_jpeg(svg_avatar)
|
32
|
+
# Save the avatar to a file (if you need to)
|
33
|
+
```
|
34
|
+
|
35
|
+
## Development
|
36
|
+
|
37
|
+
After checking out the repo:
|
38
|
+
|
39
|
+
bundle install
|
40
|
+
./viewer
|
41
|
+
|
42
|
+
This will bring up a development server on http://localhost:9292 where you can reload the page and keep generating new avatars.
|
43
|
+
|
44
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
45
|
+
|
46
|
+
## Contributing
|
47
|
+
|
48
|
+
Bug reports and pull requests are welcome on GitHub. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](CODE_OF_CONDUCT.md).
|
49
|
+
|
50
|
+
## License
|
51
|
+
|
52
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g>
|
3
|
+
<!-- Definitions for the gradients -->
|
4
|
+
<defs>
|
5
|
+
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="0%">
|
6
|
+
<stop offset="0%" stop-color="{{color1}}" />
|
7
|
+
<stop offset="100%" stop-color="{{color2}}" />
|
8
|
+
</linearGradient>
|
9
|
+
<linearGradient id="gradient2" x1="0%" y1="0%" x2="100%" y2="0%">
|
10
|
+
<stop offset="0%" stop-color="{{color3}}" />
|
11
|
+
<stop offset="100%" stop-color="{{color4}}" />
|
12
|
+
</linearGradient>
|
13
|
+
</defs>
|
14
|
+
|
15
|
+
<!-- Three horizontal bands with gradients -->
|
16
|
+
<rect x="0" y="0" width="512" height="256" fill="url(#gradient1)" />
|
17
|
+
<rect x="0" y="256" width="512" height="256" fill="url(#gradient2)" />
|
18
|
+
</g>
|
19
|
+
</svg>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g>
|
3
|
+
<!-- Definitions for the gradients -->
|
4
|
+
<defs>
|
5
|
+
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="0%">
|
6
|
+
<stop offset="0%" stop-color="{{color1}}" />
|
7
|
+
<stop offset="100%" stop-color="{{color2}}" />
|
8
|
+
</linearGradient>
|
9
|
+
<linearGradient id="gradient2" x1="0%" y1="0%" x2="100%" y2="0%">
|
10
|
+
<stop offset="0%" stop-color="{{color3}}" />
|
11
|
+
<stop offset="100%" stop-color="{{color4}}" />
|
12
|
+
</linearGradient>
|
13
|
+
<linearGradient id="gradient3" x1="0%" y1="0%" x2="100%" y2="0%">
|
14
|
+
<stop offset="0%" stop-color="{{color5}}" />
|
15
|
+
<stop offset="100%" stop-color="{{color6}}" />
|
16
|
+
</linearGradient>
|
17
|
+
</defs>
|
18
|
+
|
19
|
+
<!-- Three horizontal bands with gradients -->
|
20
|
+
<rect x="0" y="0" width="512" height="170" fill="url(#gradient1)" />
|
21
|
+
<rect x="0" y="170" width="512" height="172" fill="url(#gradient2)" />
|
22
|
+
<rect x="0" y="342" width="512" height="170" fill="url(#gradient3)" />
|
23
|
+
</g>
|
24
|
+
</svg>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g>
|
3
|
+
<defs>
|
4
|
+
<linearGradient id="gradient1" x1="0%" y1="100%" x2="100%" y2="0%">
|
5
|
+
<stop offset="0%" stop-color="{{color1}}" />
|
6
|
+
<stop offset="100%" stop-color="{{color2}}" />
|
7
|
+
</linearGradient>
|
8
|
+
</defs>
|
9
|
+
|
10
|
+
<rect x="0" y="0" width="512" height="512" fill="url(#gradient1)" />
|
11
|
+
</g>
|
12
|
+
</svg>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g>
|
3
|
+
<defs>
|
4
|
+
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="0%">
|
5
|
+
<stop offset="0%" stop-color="{{color1}}" />
|
6
|
+
<stop offset="100%" stop-color="{{color2}}" />
|
7
|
+
</linearGradient>
|
8
|
+
</defs>
|
9
|
+
|
10
|
+
<rect x="0" y="0" width="512" height="512" fill="url(#gradient1)" />
|
11
|
+
</g>
|
12
|
+
</svg>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g>
|
3
|
+
<defs>
|
4
|
+
<linearGradient id="gradient1" x1="0%" y1="0%" x2="100%" y2="100%">
|
5
|
+
<stop offset="0%" stop-color="{{color1}}" />
|
6
|
+
<stop offset="100%" stop-color="{{color2}}" />
|
7
|
+
</linearGradient>
|
8
|
+
</defs>
|
9
|
+
|
10
|
+
<rect x="0" y="0" width="512" height="512" fill="url(#gradient1)" />
|
11
|
+
</g>
|
12
|
+
</svg>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g>
|
3
|
+
<defs>
|
4
|
+
<linearGradient id="gradient1" x1="0%" y1="0%" x2="0%" y2="100%">
|
5
|
+
<stop offset="0%" stop-color="{{color1}}" />
|
6
|
+
<stop offset="100%" stop-color="{{color2}}" />
|
7
|
+
</linearGradient>
|
8
|
+
</defs>
|
9
|
+
|
10
|
+
<rect x="0" y="0" width="512" height="512" fill="url(#gradient1)" />
|
11
|
+
</g>
|
12
|
+
</svg>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g transform="{{transformation}}">
|
3
|
+
<path
|
4
|
+
d="M 276.506 38.653 C 251.084 38.653 226.657 44.164 203.86 54.93 C 174.909 68.413 150.391 89.677 132.93 116.455 C 114.927 144.047 105.427 175.983 105.427 209.005 L 105.427 232.257 L 74.668 291.062 C 68.787 302.371 68.515 313.951 73.944 322.998 C 79.372 332.044 89.776 337.201 102.532 337.201 L 105.427 337.201 L 105.427 383.432 C 105.427 408.493 125.784 428.849 150.843 428.849 C 151.477 428.849 152.02 428.849 152.651 428.757 L 184.226 424.052 L 184.226 455.807 C 184.226 456.261 184.226 456.805 184.317 457.257 C 185.222 468.475 194.27 476.798 205.759 476.798 C 207.117 476.798 208.473 476.708 209.92 476.435 L 374.126 447.304 C 386.066 445.223 395.476 434.006 395.476 421.882 L 395.476 330.506 C 428.137 298.573 446.682 254.512 446.682 208.823 C 446.682 115.01 370.324 38.653 276.506 38.653 Z M 374.94 316.213 C 372.405 318.566 370.959 321.822 370.959 325.168 L 370.959 421.702 C 370.867 422.154 370.235 422.968 369.783 423.149 L 208.563 451.646 L 208.473 409.578 C 208.473 406.049 206.935 402.613 204.22 400.35 C 201.96 398.45 199.154 397.364 196.26 397.364 C 195.626 397.364 195.084 397.364 194.449 397.456 L 149.849 404.059 C 138.631 403.607 129.673 394.379 129.673 383.07 L 129.673 324.626 C 129.673 317.841 124.245 312.412 117.46 312.412 L 102.532 312.412 C 97.918 312.412 95.476 311.147 94.843 310.06 C 94.208 308.974 94.12 306.171 96.289 302.099 L 128.496 240.67 C 129.402 238.95 129.855 236.96 129.855 234.97 L 129.855 208.733 C 129.855 152.275 162.966 100.529 214.174 76.737 L 214.264 76.737 C 233.715 67.691 254.704 63.073 276.506 63.073 C 356.845 63.073 422.256 128.484 422.256 208.823 C 422.256 249.537 404.976 288.71 374.94 316.213 Z"
|
5
|
+
fill="{{color}}" transform="matrix(1, 0, 0, 1, -1.4210854715202004e-14, 0)" />
|
6
|
+
</g>
|
7
|
+
</svg>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g transform="{{transformation}}">
|
3
|
+
<path
|
4
|
+
d="M 276.506 38.653 C 251.084 38.653 226.657 44.164 203.86 54.93 C 174.909 68.413 150.391 89.677 132.93 116.455 C 114.927 144.047 105.427 175.983 105.427 209.005 L 105.427 232.257 L 74.668 291.062 C 68.787 302.371 68.515 313.951 73.944 322.998 C 79.372 332.044 89.776 337.201 102.532 337.201 L 105.427 337.201 L 105.427 383.432 C 105.427 408.493 125.784 428.849 150.843 428.849 C 151.477 428.849 152.02 428.849 152.651 428.757 L 184.226 424.052 L 184.226 455.807 C 184.226 456.261 184.226 456.805 184.317 457.257 C 185.222 468.475 194.27 476.798 205.759 476.798 C 207.117 476.798 208.473 476.708 209.92 476.435 L 374.126 447.304 C 386.066 445.223 395.476 434.006 395.476 421.882 L 395.476 330.506 C 428.137 298.573 446.682 254.512 446.682 208.823 C 446.682 115.01 370.324 38.653 276.506 38.653 Z"
|
5
|
+
fill="{{color}}" transform="matrix(1, 0, 0, 1, -1.4210854715202004e-14, 0)" />
|
6
|
+
</g>
|
7
|
+
</svg>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g transform="{{transformation}}">
|
3
|
+
<path
|
4
|
+
d="M204.6,20.6c-46,3.2-86.2,19.6-113,46c-10.8,10.8-17.6,19.6-24.8,32.6C53.6,122.8,46,155.2,46,186.6c0,24.8,5.8,41.6,28.2,80.4c18.4,31.8,24.2,44,31,63.4c7.4,22,10.6,41.4,10.6,67c0,25.2-3.6,48.4-11,76c-1.8,6.4-3.2,12.2-3.2,12.8c0,0.6,1,2.2,2.4,3.4l2,1.8h108.4h108.4l2.2-2.4c2.4-2.2,2.4-2.4,2.4-19.2c0-18.4,1.6-33.8,4.4-41c1.6-4.2,2-4.6,5.8-5.8c2.2-0.6,11.8-1.6,21.2-2.2c28.2-1.6,39.8-3.8,51-9.4c6.4-3.4,13.6-10,16.4-15.6c2.8-5.4,5-16.2,4.2-21.2c-0.2-2.4-1.4-7-2.4-10.6l-2-6.2l4.8-4.6c5.8-5.8,6.6-8.8,3.8-15.4l-2-4.8l2.4-2.6c5.2-5.4,5.4-9.4,1.4-21.2c-1.6-4.8-2.6-8.8-2.4-9c0.2-0.2,2.8-0.8,5.6-1.2c14.2-2.4,26.4-12.8,26.4-22.6c0-4-3.6-11.2-11.2-22.6c-3.2-4.8-11.2-16.6-17.6-26.2c-6.4-9.4-12-18.2-12.2-19.6c-0.4-1.6,0.2-3.6,2.2-6.8c3.8-6,4.8-9.6,4.8-19.6c0-18-4.8-42.2-11.6-59c-19-47.2-61.4-80.2-122-95C271.2,21.6,233.8,18.6,204.6,20.6z"
|
5
|
+
fill="{{color}}" transform="matrix(1, 0, 0, 1.053, 0, -0.529683)" />
|
6
|
+
</g>
|
7
|
+
</svg>
|
@@ -0,0 +1,9 @@
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
2
|
+
<g transform="{{transformation}}">
|
3
|
+
<path
|
4
|
+
d="M258.374,20c-27.9,0-54.2,6.2-77.8,17.4c-62.2,28.9-105.4,91.9-105.4,165l0,0v24.3v5.9l-37.2,70.9
|
5
|
+
c-9.4,17.9-0.5,32.5,19.7,32.5h17.5v0.4v30.8v36.1c0,21.1,17.2,38.3,38.3,38.3l52.6-7.8l0.1,52.6v0.4l0,0c0.3,6.7,6,11.3,12.9,10
|
6
|
+
l189.4-33.6c7.2-1.3,12.9-8.2,12.9-15.5v-8.3v-6.6v-96.4c36.2-33.4,59.1-81.2,59.1-134.4C440.574,99.7,359.174,20,258.374,20z"
|
7
|
+
fill="{{color}}" />
|
8
|
+
</g>
|
9
|
+
</svg>
|
@@ -0,0 +1,346 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DefaultAvatarGenerator
|
4
|
+
# List of colors
|
5
|
+
class Colors
|
6
|
+
class << self
|
7
|
+
def all
|
8
|
+
COLORS
|
9
|
+
end
|
10
|
+
|
11
|
+
def get(color_name, shade = 500)
|
12
|
+
COLORS.dig(color_name.to_sym, shade)
|
13
|
+
end
|
14
|
+
|
15
|
+
def available_colors
|
16
|
+
COLORS.keys
|
17
|
+
end
|
18
|
+
|
19
|
+
def select_solid_color
|
20
|
+
get(available_colors.sample, solid_shades.sample)
|
21
|
+
end
|
22
|
+
|
23
|
+
def select_contrast_color
|
24
|
+
get(available_colors.sample, contrast_shades.sample)
|
25
|
+
end
|
26
|
+
|
27
|
+
def available_shades
|
28
|
+
[50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950]
|
29
|
+
end
|
30
|
+
|
31
|
+
def solid_shades
|
32
|
+
[300, 400, 500, 600, 700, 800, 900]
|
33
|
+
end
|
34
|
+
|
35
|
+
def contrast_shades
|
36
|
+
[50, 100, 950]
|
37
|
+
end
|
38
|
+
|
39
|
+
def opposite_shade(shade)
|
40
|
+
shade_map = {
|
41
|
+
50 => 950,
|
42
|
+
100 => 900,
|
43
|
+
200 => 800,
|
44
|
+
300 => 700,
|
45
|
+
400 => 600,
|
46
|
+
500 => 500,
|
47
|
+
600 => 400,
|
48
|
+
700 => 300,
|
49
|
+
800 => 200,
|
50
|
+
900 => 100,
|
51
|
+
950 => 50
|
52
|
+
}
|
53
|
+
shade_map[shade]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
COLORS = {
|
58
|
+
slate: {
|
59
|
+
50 => '#f8fafc',
|
60
|
+
100 => '#f1f5f9',
|
61
|
+
200 => '#e2e8f0',
|
62
|
+
300 => '#cbd5e1',
|
63
|
+
400 => '#94a3b8',
|
64
|
+
500 => '#64748b',
|
65
|
+
600 => '#475569',
|
66
|
+
700 => '#334155',
|
67
|
+
800 => '#1e293b',
|
68
|
+
900 => '#0f172a',
|
69
|
+
950 => '#020617'
|
70
|
+
},
|
71
|
+
gray: {
|
72
|
+
50 => '#f9fafb',
|
73
|
+
100 => '#f3f4f6',
|
74
|
+
200 => '#e5e7eb',
|
75
|
+
300 => '#d1d5db',
|
76
|
+
400 => '#9ca3af',
|
77
|
+
500 => '#6b7280',
|
78
|
+
600 => '#4b5563',
|
79
|
+
700 => '#374151',
|
80
|
+
800 => '#1f2937',
|
81
|
+
900 => '#111827',
|
82
|
+
950 => '#030712'
|
83
|
+
},
|
84
|
+
zinc: {
|
85
|
+
50 => '#fafafa',
|
86
|
+
100 => '#f4f4f5',
|
87
|
+
200 => '#e4e4e7',
|
88
|
+
300 => '#d4d4d8',
|
89
|
+
400 => '#a1a1aa',
|
90
|
+
500 => '#71717a',
|
91
|
+
600 => '#52525b',
|
92
|
+
700 => '#3f3f46',
|
93
|
+
800 => '#27272a',
|
94
|
+
900 => '#18181b',
|
95
|
+
950 => '#09090b'
|
96
|
+
},
|
97
|
+
neutral: {
|
98
|
+
50 => '#fafafa',
|
99
|
+
100 => '#f5f5f5',
|
100
|
+
200 => '#e5e5e5',
|
101
|
+
300 => '#d4d4d4',
|
102
|
+
400 => '#a3a3a3',
|
103
|
+
500 => '#737373',
|
104
|
+
600 => '#525252',
|
105
|
+
700 => '#404040',
|
106
|
+
800 => '#262626',
|
107
|
+
900 => '#171717',
|
108
|
+
950 => '#0a0a0a'
|
109
|
+
},
|
110
|
+
stone: {
|
111
|
+
50 => '#fafaf9',
|
112
|
+
100 => '#f5f5f4',
|
113
|
+
200 => '#e7e5e4',
|
114
|
+
300 => '#d6d3d1',
|
115
|
+
400 => '#a8a29e',
|
116
|
+
500 => '#78716c',
|
117
|
+
600 => '#57534e',
|
118
|
+
700 => '#44403c',
|
119
|
+
800 => '#292524',
|
120
|
+
900 => '#1c1917',
|
121
|
+
950 => '#0c0a09'
|
122
|
+
},
|
123
|
+
red: {
|
124
|
+
50 => '#fef2f2',
|
125
|
+
100 => '#fee2e2',
|
126
|
+
200 => '#fecaca',
|
127
|
+
300 => '#fca5a5',
|
128
|
+
400 => '#f87171',
|
129
|
+
500 => '#ef4444',
|
130
|
+
600 => '#dc2626',
|
131
|
+
700 => '#b91c1c',
|
132
|
+
800 => '#991b1b',
|
133
|
+
900 => '#7f1d1d',
|
134
|
+
950 => '#450a0a'
|
135
|
+
},
|
136
|
+
orange: {
|
137
|
+
50 => '#fff7ed',
|
138
|
+
100 => '#ffedd5',
|
139
|
+
200 => '#fed7aa',
|
140
|
+
300 => '#fdba74',
|
141
|
+
400 => '#fb923c',
|
142
|
+
500 => '#f97316',
|
143
|
+
600 => '#ea580c',
|
144
|
+
700 => '#c2410c',
|
145
|
+
800 => '#9a3412',
|
146
|
+
900 => '#7c2d12',
|
147
|
+
950 => '#431407'
|
148
|
+
},
|
149
|
+
amber: {
|
150
|
+
50 => '#fffbeb',
|
151
|
+
100 => '#fef3c7',
|
152
|
+
200 => '#fde68a',
|
153
|
+
300 => '#fcd34d',
|
154
|
+
400 => '#fbbf24',
|
155
|
+
500 => '#f59e0b',
|
156
|
+
600 => '#d97706',
|
157
|
+
700 => '#b45309',
|
158
|
+
800 => '#92400e',
|
159
|
+
900 => '#78350f',
|
160
|
+
950 => '#451a03'
|
161
|
+
},
|
162
|
+
yellow: {
|
163
|
+
50 => '#fefce8',
|
164
|
+
100 => '#fef9c3',
|
165
|
+
200 => '#fef08a',
|
166
|
+
300 => '#fde047',
|
167
|
+
400 => '#facc15',
|
168
|
+
500 => '#eab308',
|
169
|
+
600 => '#ca8a04',
|
170
|
+
700 => '#a16207',
|
171
|
+
800 => '#854d0e',
|
172
|
+
900 => '#713f12',
|
173
|
+
950 => '#422006'
|
174
|
+
},
|
175
|
+
lime: {
|
176
|
+
50 => '#f7fee7',
|
177
|
+
100 => '#ecfccb',
|
178
|
+
200 => '#d9f99d',
|
179
|
+
300 => '#bef264',
|
180
|
+
400 => '#a3e635',
|
181
|
+
500 => '#84cc16',
|
182
|
+
600 => '#65a30d',
|
183
|
+
700 => '#4d7c0f',
|
184
|
+
800 => '#3f6212',
|
185
|
+
900 => '#365314',
|
186
|
+
950 => '#1a2e05'
|
187
|
+
},
|
188
|
+
green: {
|
189
|
+
50 => '#f0fdf4',
|
190
|
+
100 => '#dcfce7',
|
191
|
+
200 => '#bbf7d0',
|
192
|
+
300 => '#86efac',
|
193
|
+
400 => '#4ade80',
|
194
|
+
500 => '#22c55e',
|
195
|
+
600 => '#16a34a',
|
196
|
+
700 => '#15803d',
|
197
|
+
800 => '#166534',
|
198
|
+
900 => '#14532d',
|
199
|
+
950 => '#052e16'
|
200
|
+
},
|
201
|
+
emerald: {
|
202
|
+
50 => '#ecfdf5',
|
203
|
+
100 => '#d1fae5',
|
204
|
+
200 => '#a7f3d0',
|
205
|
+
300 => '#6ee7b7',
|
206
|
+
400 => '#34d399',
|
207
|
+
500 => '#10b981',
|
208
|
+
600 => '#059669',
|
209
|
+
700 => '#047857',
|
210
|
+
800 => '#065f46',
|
211
|
+
900 => '#064e3b',
|
212
|
+
950 => '#022c22'
|
213
|
+
},
|
214
|
+
teal: {
|
215
|
+
50 => '#f0fdfa',
|
216
|
+
100 => '#ccfbf1',
|
217
|
+
200 => '#99f6e4',
|
218
|
+
300 => '#5eead4',
|
219
|
+
400 => '#2dd4bf',
|
220
|
+
500 => '#14b8a6',
|
221
|
+
600 => '#0d9488',
|
222
|
+
700 => '#0f766e',
|
223
|
+
800 => '#115e59',
|
224
|
+
900 => '#134e4a',
|
225
|
+
950 => '#042f2e'
|
226
|
+
},
|
227
|
+
cyan: {
|
228
|
+
50 => '#ecfeff',
|
229
|
+
100 => '#cffafe',
|
230
|
+
200 => '#a5f3fc',
|
231
|
+
300 => '#67e8f9',
|
232
|
+
400 => '#22d3ee',
|
233
|
+
500 => '#06b6d4',
|
234
|
+
600 => '#0891b2',
|
235
|
+
700 => '#0e7490',
|
236
|
+
800 => '#155e75',
|
237
|
+
900 => '#164e63',
|
238
|
+
950 => '#083344'
|
239
|
+
},
|
240
|
+
sky: {
|
241
|
+
50 => '#f0f9ff',
|
242
|
+
100 => '#e0f2fe',
|
243
|
+
200 => '#bae6fd',
|
244
|
+
300 => '#7dd3fc',
|
245
|
+
400 => '#38bdf8',
|
246
|
+
500 => '#0ea5e9',
|
247
|
+
600 => '#0284c7',
|
248
|
+
700 => '#0369a1',
|
249
|
+
800 => '#075985',
|
250
|
+
900 => '#0c4a6e',
|
251
|
+
950 => '#082f49'
|
252
|
+
},
|
253
|
+
blue: {
|
254
|
+
50 => '#eff6ff',
|
255
|
+
100 => '#dbeafe',
|
256
|
+
200 => '#bfdbfe',
|
257
|
+
300 => '#93c5fd',
|
258
|
+
400 => '#60a5fa',
|
259
|
+
500 => '#3b82f6',
|
260
|
+
600 => '#2563eb',
|
261
|
+
700 => '#1d4ed8',
|
262
|
+
800 => '#1e40af',
|
263
|
+
900 => '#1e3a8a',
|
264
|
+
950 => '#172554'
|
265
|
+
},
|
266
|
+
indigo: {
|
267
|
+
50 => '#eef2ff',
|
268
|
+
100 => '#e0e7ff',
|
269
|
+
200 => '#c7d2fe',
|
270
|
+
300 => '#a5b4fc',
|
271
|
+
400 => '#818cf8',
|
272
|
+
500 => '#6366f1',
|
273
|
+
600 => '#4f46e5',
|
274
|
+
700 => '#4338ca',
|
275
|
+
800 => '#3730a3',
|
276
|
+
900 => '#312e81',
|
277
|
+
950 => '#1e1b4b'
|
278
|
+
},
|
279
|
+
violet: {
|
280
|
+
50 => '#f5f3ff',
|
281
|
+
100 => '#ede9fe',
|
282
|
+
200 => '#ddd6fe',
|
283
|
+
300 => '#c4b5fd',
|
284
|
+
400 => '#a78bfa',
|
285
|
+
500 => '#8b5cf6',
|
286
|
+
600 => '#7c3aed',
|
287
|
+
700 => '#6d28d9',
|
288
|
+
800 => '#5b21b6',
|
289
|
+
900 => '#4c1d95',
|
290
|
+
950 => '#2e1065'
|
291
|
+
},
|
292
|
+
purple: {
|
293
|
+
50 => '#faf5ff',
|
294
|
+
100 => '#f3e8ff',
|
295
|
+
200 => '#e9d5ff',
|
296
|
+
300 => '#d8b4fe',
|
297
|
+
400 => '#c084fc',
|
298
|
+
500 => '#a855f7',
|
299
|
+
600 => '#9333ea',
|
300
|
+
700 => '#7e22ce',
|
301
|
+
800 => '#6b21a8',
|
302
|
+
900 => '#581c87',
|
303
|
+
950 => '#3b0764'
|
304
|
+
},
|
305
|
+
fuchsia: {
|
306
|
+
50 => '#fdf4ff',
|
307
|
+
100 => '#fae8ff',
|
308
|
+
200 => '#f5d0fe',
|
309
|
+
300 => '#f0abfc',
|
310
|
+
400 => '#e879f9',
|
311
|
+
500 => '#d946ef',
|
312
|
+
600 => '#c026d3',
|
313
|
+
700 => '#a21caf',
|
314
|
+
800 => '#86198f',
|
315
|
+
900 => '#701a75',
|
316
|
+
950 => '#4a044e'
|
317
|
+
},
|
318
|
+
pink: {
|
319
|
+
50 => '#fdf2f8',
|
320
|
+
100 => '#fce7f3',
|
321
|
+
200 => '#fbcfe8',
|
322
|
+
300 => '#f9a8d4',
|
323
|
+
400 => '#f472b6',
|
324
|
+
500 => '#ec4899',
|
325
|
+
600 => '#db2777',
|
326
|
+
700 => '#be185d',
|
327
|
+
800 => '#9d174d',
|
328
|
+
900 => '#831843',
|
329
|
+
950 => '#500724'
|
330
|
+
},
|
331
|
+
rose: {
|
332
|
+
50 => '#fff1f2',
|
333
|
+
100 => '#ffe4e6',
|
334
|
+
200 => '#fecdd3',
|
335
|
+
300 => '#fda4af',
|
336
|
+
400 => '#fb7185',
|
337
|
+
500 => '#f43f5e',
|
338
|
+
600 => '#e11d48',
|
339
|
+
700 => '#be123c',
|
340
|
+
800 => '#9f1239',
|
341
|
+
900 => '#881337',
|
342
|
+
950 => '#4c0519'
|
343
|
+
}
|
344
|
+
}.freeze
|
345
|
+
end
|
346
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DefaultAvatarGenerator
|
4
|
+
# Composes all of the layers together into a single SVG
|
5
|
+
class Composer
|
6
|
+
def initialize(layers)
|
7
|
+
@layers = layers
|
8
|
+
end
|
9
|
+
|
10
|
+
def compose
|
11
|
+
SvgUtils.minify(<<~SVG)
|
12
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
13
|
+
#{@layers.map(&:render).join("\n ")}
|
14
|
+
</svg>
|
15
|
+
SVG
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DefaultAvatarGenerator
|
4
|
+
# Foreground SVG layer
|
5
|
+
class ForegroundLayer < Layer
|
6
|
+
def self.select_transform
|
7
|
+
['', 'scale(-1, 1) translate(-512, 0)'].sample
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def template_path
|
13
|
+
File.join(__dir__, '..', 'assets', 'foregrounds', "#{template_name}.svg")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DefaultAvatarGenerator
|
4
|
+
# The Generator class is responsible for creating avatar images with customizable
|
5
|
+
# backgrounds, foregrounds, and text layers.
|
6
|
+
class Generator
|
7
|
+
AVAILABLE_BACKGROUNDS = %w[
|
8
|
+
gradient-2h
|
9
|
+
gradient-3h
|
10
|
+
gradient-backslash
|
11
|
+
gradient-slash
|
12
|
+
gradient-lr
|
13
|
+
gradient-tb
|
14
|
+
solid
|
15
|
+
].freeze
|
16
|
+
|
17
|
+
AVAILABLE_FOREGROUNDS = %w[
|
18
|
+
head1-solid
|
19
|
+
head2
|
20
|
+
head3
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
# NOTE: 'base' doesn't work because libvips doesn't support `dominant-baseline` in SVG.
|
24
|
+
# Can move back once https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/1072
|
25
|
+
# makes it way into the latest version.
|
26
|
+
AVAILABLE_TEXT = %w[
|
27
|
+
hack
|
28
|
+
].freeze
|
29
|
+
|
30
|
+
def initialize(options = {})
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
def generate # rubocop:disable Metrics/AbcSize
|
35
|
+
# Create layers with dynamic parameters
|
36
|
+
layers = [
|
37
|
+
BackgroundLayer.new(
|
38
|
+
select_background,
|
39
|
+
background_params.merge(@options[:background] || {})
|
40
|
+
),
|
41
|
+
ForegroundLayer.new(
|
42
|
+
select_foreground,
|
43
|
+
foreground_params.merge(@options[:foreground] || {})
|
44
|
+
),
|
45
|
+
TextLayer.new(
|
46
|
+
select_text,
|
47
|
+
text_params.merge(@options[:text] || {})
|
48
|
+
)
|
49
|
+
]
|
50
|
+
|
51
|
+
Composer.new(layers).compose
|
52
|
+
end
|
53
|
+
|
54
|
+
def select_background
|
55
|
+
AVAILABLE_BACKGROUNDS.sample
|
56
|
+
end
|
57
|
+
|
58
|
+
def background_params
|
59
|
+
{
|
60
|
+
color1: Colors.select_solid_color,
|
61
|
+
color2: Colors.select_solid_color,
|
62
|
+
color3: Colors.select_solid_color,
|
63
|
+
color4: Colors.select_solid_color,
|
64
|
+
color5: Colors.select_solid_color,
|
65
|
+
color6: Colors.select_solid_color
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
def select_foreground
|
70
|
+
AVAILABLE_FOREGROUNDS.sample
|
71
|
+
end
|
72
|
+
|
73
|
+
def foreground_params
|
74
|
+
@foreground_color_name = Colors.available_colors.sample
|
75
|
+
@foreground_shade = Colors.contrast_shades.sample
|
76
|
+
{
|
77
|
+
color: Colors.get(@foreground_color_name, @foreground_shade),
|
78
|
+
transformation: ForegroundLayer.select_transform
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def select_text
|
83
|
+
AVAILABLE_TEXT.sample
|
84
|
+
end
|
85
|
+
|
86
|
+
def text_params
|
87
|
+
{
|
88
|
+
color: Colors.get(@foreground_color_name, Colors.opposite_shade(@foreground_shade))
|
89
|
+
# character: ('A'..'Z').to_a.sample
|
90
|
+
}
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
module DefaultAvatarGenerator
|
6
|
+
# Converts SVG to other image formats
|
7
|
+
class ImageConverter
|
8
|
+
def self.svg_to_jpeg(svg_content)
|
9
|
+
# Create a temporary file to store the SVG
|
10
|
+
temp_svg = Tempfile.new(['avatar', '.svg'])
|
11
|
+
begin
|
12
|
+
temp_svg.write(svg_content)
|
13
|
+
temp_svg.close
|
14
|
+
|
15
|
+
# Load the SVG using vips and convert to JPEG
|
16
|
+
image = Vips::Image.new_from_file(temp_svg.path)
|
17
|
+
|
18
|
+
# Convert to sRGB color space and save as JPEG to memory
|
19
|
+
image = image.colourspace('srgb')
|
20
|
+
image.jpegsave_buffer(Q: 90)
|
21
|
+
ensure
|
22
|
+
# Clean up temporary file
|
23
|
+
temp_svg.unlink
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module DefaultAvatarGenerator
|
6
|
+
# The Layer class is responsible for rendering SVG templates with given parameters.
|
7
|
+
class Layer
|
8
|
+
attr_reader :template_name, :params
|
9
|
+
|
10
|
+
def initialize(template_name, params = {})
|
11
|
+
@template_name = template_name
|
12
|
+
@params = params
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
template = load_template
|
17
|
+
process_template(template)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def load_template
|
23
|
+
# Load the SVG template file
|
24
|
+
File.read(template_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def template_path
|
28
|
+
# Implement in subclasses to define where templates are stored
|
29
|
+
raise NotImplementedError, "#{self.class} must implement #template_path"
|
30
|
+
end
|
31
|
+
|
32
|
+
def process_template(template)
|
33
|
+
# Replace only the parameters that exist in params
|
34
|
+
parsed = template.gsub(/\{\{(\w+)\}\}/) do |match|
|
35
|
+
param_name = ::Regexp.last_match(1)
|
36
|
+
params.key?(param_name.to_sym) ? params[param_name.to_sym].to_s : match
|
37
|
+
end
|
38
|
+
|
39
|
+
# Parse the XML and extract just the first child element inside the svg tag
|
40
|
+
doc = Nokogiri::XML(parsed)
|
41
|
+
doc.at('svg > *').to_s
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'vips'
|
4
|
+
require_relative 'default_avatar_generator/version'
|
5
|
+
require_relative 'default_avatar_generator/colors'
|
6
|
+
require_relative 'default_avatar_generator/layer'
|
7
|
+
require_relative 'default_avatar_generator/background_layer'
|
8
|
+
require_relative 'default_avatar_generator/foreground_layer'
|
9
|
+
require_relative 'default_avatar_generator/text_layer'
|
10
|
+
require_relative 'default_avatar_generator/svg_utils'
|
11
|
+
require_relative 'default_avatar_generator/composer'
|
12
|
+
require_relative 'default_avatar_generator/generator'
|
13
|
+
require_relative 'default_avatar_generator/image_converter'
|
14
|
+
|
15
|
+
module DefaultAvatarGenerator
|
16
|
+
class Error < StandardError; end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: default-avatar-generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Peter Sankauskas
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 2025-04-09 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: nokogiri
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '1.18'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '1.18'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: ruby-vips
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.2'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.2'
|
40
|
+
description: Generates beautiful and unique default avatars for user accounts
|
41
|
+
email:
|
42
|
+
- peter+avatar@pas.ventures
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- CHANGELOG.md
|
48
|
+
- README.md
|
49
|
+
- lib/assets/backgrounds/gradient-2h.svg
|
50
|
+
- lib/assets/backgrounds/gradient-3h.svg
|
51
|
+
- lib/assets/backgrounds/gradient-backslash.svg
|
52
|
+
- lib/assets/backgrounds/gradient-lr.svg
|
53
|
+
- lib/assets/backgrounds/gradient-slash.svg
|
54
|
+
- lib/assets/backgrounds/gradient-tb.svg
|
55
|
+
- lib/assets/backgrounds/solid.svg
|
56
|
+
- lib/assets/foregrounds/head1-hollow.svg
|
57
|
+
- lib/assets/foregrounds/head1-solid.svg
|
58
|
+
- lib/assets/foregrounds/head2.svg
|
59
|
+
- lib/assets/foregrounds/head3.svg
|
60
|
+
- lib/assets/text/base.svg
|
61
|
+
- lib/assets/text/hack.svg
|
62
|
+
- lib/default_avatar_generator.rb
|
63
|
+
- lib/default_avatar_generator/background_layer.rb
|
64
|
+
- lib/default_avatar_generator/colors.rb
|
65
|
+
- lib/default_avatar_generator/composer.rb
|
66
|
+
- lib/default_avatar_generator/foreground_layer.rb
|
67
|
+
- lib/default_avatar_generator/generator.rb
|
68
|
+
- lib/default_avatar_generator/image_converter.rb
|
69
|
+
- lib/default_avatar_generator/layer.rb
|
70
|
+
- lib/default_avatar_generator/svg_utils.rb
|
71
|
+
- lib/default_avatar_generator/text_layer.rb
|
72
|
+
- lib/default_avatar_generator/version.rb
|
73
|
+
homepage: https://github.com/pas256/default-avatar-generator
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata:
|
77
|
+
homepage_uri: https://github.com/pas256/default-avatar-generator
|
78
|
+
source_code_uri: https://github.com/pas256/default-avatar-generator
|
79
|
+
changelog_uri: https://github.com/pas256/default-avatar-generator/blob/main/CHANGELOG.md
|
80
|
+
rubygems_mfa_required: 'true'
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 3.0.0
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubygems_version: 3.6.2
|
96
|
+
specification_version: 4
|
97
|
+
summary: Generate unique, default avatars for user accounts
|
98
|
+
test_files: []
|