├── fonts
├── rockwell.ttf
└── OpenSans-Bold.ttf
├── src
├── Generator
│ ├── GeneratorInterface.php
│ └── DefaultGenerator.php
├── Facade.php
├── Concerns
│ ├── AttributeGetter.php
│ ├── AttributeSetter.php
│ ├── ImageExport.php
│ └── StorageOptimization.php
├── LumenServiceProvider.php
├── ServiceProvider.php
├── HDAvatar.php
├── HDAvatarResponse.php
└── Avatar.php
├── CHANGELOG.md
├── LICENSE.md
├── composer.json
├── config
├── config.php
├── config-hd.php
└── hd-avatar.php
├── examples
└── hd-avatar-examples.php
└── README.md
/fonts/rockwell.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laravolt/avatar/HEAD/fonts/rockwell.ttf
--------------------------------------------------------------------------------
/fonts/OpenSans-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/laravolt/avatar/HEAD/fonts/OpenSans-Bold.ttf
--------------------------------------------------------------------------------
/src/Generator/GeneratorInterface.php:
--------------------------------------------------------------------------------
1 | $key;
12 | }
13 |
14 | /**
15 | * Get background color
16 | */
17 | public function getBackground(): string
18 | {
19 | return $this->background;
20 | }
21 |
22 | /**
23 | * Get foreground color
24 | */
25 | public function getForeground(): string
26 | {
27 | return $this->foreground;
28 | }
29 |
30 | /**
31 | * Get shape
32 | */
33 | public function getShape(): string
34 | {
35 | return $this->shape;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## 4.0.0
4 | - Change default image driver to `gd`.
5 | - Add public method `getAttribute($attribute)`
6 | - Add support for Laravel 8
7 | - Drop support for Laravel 5.6, 5.7, and 5.8
8 | - Drop support for PHP 7.1 and 7.2
9 |
10 | ## 3.0.0
11 |
12 | - Implement themes [#73](https://github.com/laravolt/avatar/issues/73)
13 | - Add `setTheme()`
14 | - Add support for Laravel 6
15 | - Drop support for Laravel 5.2, 5.3, 5.4, and 5.5
16 | - Drop support for PHP 7.0
17 |
18 | ## 2.2.1
19 |
20 | * Add `setChars()` [#75](https://github.com/laravolt/avatar/pull/75)
21 |
22 | ## 2.2.0
23 | * Add gravatar support
24 |
25 | ## 2.1.0
26 | * Add functionality to set custom font-family for SVG
27 |
28 | ## 2.0.0
29 | * support plain PHP project
30 | * remove support for php 5.x
31 | * remove font folder publishing (Laravel version)
32 | * remove `setFontFolder`
33 | * `fonts` config only accept full path
34 | * add `toSvg()`
35 | * move config file from `config/avatar.php` to `config/laravolt/avatar.php`
36 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Laravolt
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "laravolt/avatar",
3 | "description": "Turn name, email, and any other string into initial-based avatar or gravatar.",
4 | "keywords": [
5 | "laravel",
6 | "laravolt",
7 | "avatar",
8 | "gravatar"
9 | ],
10 | "homepage": "https://github.com/laravolt/avatar",
11 | "license": "MIT",
12 | "authors": [
13 | {
14 | "name": "Bayu Hendra Winata",
15 | "email": "uyab.exe@gmail.com",
16 | "homepage": "https://laravolt.dev",
17 | "role": "Developer"
18 | }
19 | ],
20 | "require": {
21 | "php": ">=8.2",
22 | "illuminate/support": "^10.0|^11.0|^12.0",
23 | "illuminate/cache": "^10.0|^11.0|^12.0",
24 | "intervention/image": "^3.4"
25 | },
26 | "suggest": {
27 | "ext-gd": "Needed to support image manipulation",
28 | "ext-imagick": "Needed to support image manipulation, better than GD"
29 | },
30 | "require-dev": {
31 | "roave/security-advisories": "dev-latest",
32 | "phpunit/phpunit": "^11.5.3",
33 | "mockery/mockery": "^1.6.7",
34 | "php-coveralls/php-coveralls": "^2.1",
35 | "pestphp/pest": "^2.34|^3.7",
36 | "pestphp/pest-plugin-type-coverage": "^2.8|^3.3"
37 | },
38 | "autoload": {
39 | "psr-4": {
40 | "Laravolt\\Avatar\\": "src"
41 | }
42 | },
43 | "autoload-dev": {
44 | "psr-4": {
45 | "Laravolt\\Avatar\\Test\\": "tests"
46 | }
47 | },
48 | "scripts": {
49 | "test": "phpunit"
50 | },
51 | "extra": {
52 | "branch-alias": {
53 | "dev-master": "6.0-dev"
54 | },
55 | "laravel": {
56 | "providers": [
57 | "Laravolt\\Avatar\\ServiceProvider"
58 | ],
59 | "aliases": {
60 | "Avatar": "Laravolt\\Avatar\\Facade"
61 | }
62 | }
63 | },
64 | "config": {
65 | "allow-plugins": {
66 | "pestphp/pest-plugin": true
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/LumenServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->bind('avatar', function (Application $app) {
25 | $config = $app->make('config');
26 | $cache = $app->make('cache.store');
27 |
28 | $avatar = new Avatar($config->get('laravolt.avatar'), $cache);
29 | $avatar->setGenerator($app['avatar.generator']);
30 |
31 | return $avatar;
32 | });
33 |
34 | $this->app->bind('avatar.generator', function (Application $app) {
35 | $config = $app->make('config');
36 | $class = $config->get('laravolt.avatar.generator');
37 |
38 | return new $class;
39 | });
40 | }
41 |
42 | public function provides()
43 | {
44 | return ['avatar', 'avatar.generator'];
45 | }
46 |
47 | /**
48 | * Application is booting.
49 | *
50 | * @return void
51 | */
52 | public function boot()
53 | {
54 | $this->registerConfigurations();
55 | }
56 |
57 | /**
58 | * Register the package configurations.
59 | *
60 | * @return void
61 | */
62 | protected function registerConfigurations()
63 | {
64 | $this->mergeConfigFrom($this->packagePath('config/config.php'), 'laravolt.avatar');
65 | $this->publishes([$this->packagePath('config/config.php') => config_path('laravolt/avatar.php')], 'config');
66 | }
67 |
68 | /**
69 | * Loads a path relative to the package base directory.
70 | *
71 | * @param string $path
72 | * @return string
73 | */
74 | protected function packagePath($path = '')
75 | {
76 | return sprintf('%s/../%s', __DIR__, $path);
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/ServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->bind('avatar', function (Application $app) {
25 | $cache = $app->make('cache.store');
26 | $config = $app['config']->get('laravolt.avatar', []);
27 |
28 | $avatar = new Avatar($config, $cache);
29 | $avatar->setGenerator($app['avatar.generator']);
30 |
31 | return $avatar;
32 | });
33 |
34 | $this->app->bind('avatar.generator', function (Application $app) {
35 | $config = $app->make('config');
36 | $class = $config->get('laravolt.avatar.generator');
37 |
38 | return new $class;
39 | });
40 | }
41 |
42 | public function provides()
43 | {
44 | return ['avatar', 'avatar.generator'];
45 | }
46 |
47 | /**
48 | * Application is booting.
49 | *
50 | * @return void
51 | */
52 | public function boot()
53 | {
54 | $this->registerConfigurations();
55 | }
56 |
57 | /**
58 | * Register the package configurations.
59 | *
60 | * @return void
61 | */
62 | protected function registerConfigurations()
63 | {
64 | $this->mergeConfigFrom($this->packagePath('config/config.php'), 'laravolt.avatar');
65 | $this->publishes([$this->packagePath('config/config.php') => config_path('laravolt/avatar.php')], 'config');
66 | $this->mergeConfigFrom($this->packagePath('config/config-hd.php'), 'laravolt.avatar.hd');
67 | $this->publishes([$this->packagePath('config/config-hd.php') => config_path('laravolt/avatar-hd.php')], 'config');
68 | }
69 |
70 | /**
71 | * Loads a path relative to the package base directory.
72 | *
73 | * @param string $path
74 | * @return string
75 | */
76 | protected function packagePath($path = '')
77 | {
78 | return sprintf('%s/../%s', __DIR__, $path);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/src/Concerns/AttributeSetter.php:
--------------------------------------------------------------------------------
1 | themes)) {
13 | return $this;
14 | }
15 |
16 | $this->theme = $theme;
17 | }
18 |
19 | $this->initTheme();
20 |
21 | return $this;
22 | }
23 |
24 | public function setBackground($hex): static
25 | {
26 | $this->background = $hex;
27 |
28 | return $this;
29 | }
30 |
31 | public function setForeground($hex): static
32 | {
33 | $this->foreground = $hex;
34 |
35 | return $this;
36 | }
37 |
38 | public function setDimension($width, $height = null): static
39 | {
40 | if (! $height) {
41 | $height = $width;
42 | }
43 | $this->width = $width;
44 | $this->height = $height;
45 |
46 | return $this;
47 | }
48 |
49 | public function setResponsive($responsive): static
50 | {
51 | $this->responsive = $responsive;
52 |
53 | return $this;
54 | }
55 |
56 | public function setFontSize($size): static
57 | {
58 | $this->fontSize = $size;
59 |
60 | return $this;
61 | }
62 |
63 | public function setFontFamily($font): static
64 | {
65 | $this->fontFamily = $font;
66 |
67 | return $this;
68 | }
69 |
70 | public function setBorder($size, $color, $radius = 0): static
71 | {
72 | $this->borderSize = $size;
73 | $this->borderColor = $color;
74 | $this->borderRadius = $radius;
75 |
76 | return $this;
77 | }
78 |
79 | public function setBorderRadius($radius): static
80 | {
81 | $this->borderRadius = $radius;
82 |
83 | return $this;
84 | }
85 |
86 | public function setShape($shape): static
87 | {
88 | $this->shape = $shape;
89 |
90 | return $this;
91 | }
92 |
93 | public function setChars($chars): static
94 | {
95 | $this->chars = $chars;
96 |
97 | return $this;
98 | }
99 |
100 | public function setFont($font): static
101 | {
102 | if (is_file($font)) {
103 | $this->font = $font;
104 | }
105 |
106 | return $this;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/Generator/DefaultGenerator.php:
--------------------------------------------------------------------------------
1 | setName($name, $ascii);
15 |
16 | $words = new Collection(explode(' ', (string) $this->name));
17 |
18 | // if name contains single word, use first N character
19 | if ($words->count() === 1) {
20 | $initial = $this->getInitialFromOneWord($words, $length);
21 | } else {
22 | $initial = $this->getInitialFromMultipleWords($words, $length);
23 | }
24 |
25 | if ($uppercase) {
26 | $initial = strtoupper($initial);
27 | }
28 |
29 | if ($rtl) {
30 | $initial = collect(mb_str_split($initial))->reverse()->implode('');
31 | }
32 |
33 | return $initial;
34 | }
35 |
36 | protected function setName(?string $name, bool $ascii): void
37 | {
38 | if (is_array($name)) {
39 | throw new \InvalidArgumentException(
40 | 'Passed value cannot be an array'
41 | );
42 | }
43 |
44 | if (is_object($name) && ! method_exists($name, '__toString')) {
45 | throw new \InvalidArgumentException(
46 | 'Passed object must have a __toString method'
47 | );
48 | }
49 |
50 | if (filter_var($name, FILTER_VALIDATE_EMAIL)) {
51 | // turn bayu.hendra@gmail.com into "Bayu Hendra"
52 | $name = str_replace('.', ' ', Str::before($name, '@'));
53 | }
54 |
55 | if ($ascii) {
56 | $name = Str::ascii($name);
57 | }
58 |
59 | $this->name = $name;
60 | }
61 |
62 | protected function getInitialFromOneWord(Collection $words, int $length): string
63 | {
64 | $initial = (string) $words->first();
65 |
66 | if (strlen((string) $this->name) >= $length) {
67 | $initial = Str::substr($this->name, 0, $length);
68 | }
69 |
70 | return $initial;
71 | }
72 |
73 | protected function getInitialFromMultipleWords(Collection $words, int $length): string
74 | {
75 | // otherwise, use initial char from each word
76 | $initials = new Collection;
77 | $words->each(function (string $word) use ($initials) {
78 | $initials->push(Str::substr($word, 0, 1));
79 | });
80 |
81 | return $this->selectInitialFromMultipleInitials($initials, $length);
82 | }
83 |
84 | protected function selectInitialFromMultipleInitials(Collection $initials, int $length): string
85 | {
86 | return $initials->slice(0, $length)->implode('');
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/config/config.php:
--------------------------------------------------------------------------------
1 | env('IMAGE_DRIVER', 'gd'),
21 |
22 | /*
23 | |--------------------------------------------------------------------------
24 | | Cache Configuration
25 | |--------------------------------------------------------------------------
26 | | Control caching behavior for avatars
27 | |
28 | */
29 | 'cache' => [
30 | // Set to true to enable caching, false to disable
31 | 'enabled' => env('AVATAR_CACHE_ENABLED', true),
32 |
33 | // Cache prefix to avoid conflicts with other cached items
34 | 'key_prefix' => 'avatar_',
35 |
36 | // Cache duration in seconds
37 | // Set to null to cache forever, 0 to disable cache
38 | // Default: 86400 (24 hours)
39 | 'duration' => env('AVATAR_CACHE_DURATION', 86400),
40 | ],
41 |
42 | // Initial generator class
43 | 'generator' => \Laravolt\Avatar\Generator\DefaultGenerator::class,
44 |
45 | // Whether all characters supplied must be replaced with their closest ASCII counterparts
46 | 'ascii' => false,
47 |
48 | // Image shape: circle or square
49 | 'shape' => 'circle',
50 |
51 | // Image width, in pixel
52 | 'width' => 100,
53 |
54 | // Image height, in pixel
55 | 'height' => 100,
56 |
57 | // Responsive SVG, height and width attributes are not added when true
58 | 'responsive' => false,
59 |
60 | // Number of characters used as initials. If name consists of single word, the first N character will be used
61 | 'chars' => 2,
62 |
63 | // font size
64 | 'fontSize' => 48,
65 |
66 | // convert initial letter in uppercase
67 | 'uppercase' => false,
68 |
69 | // Right to Left (RTL)
70 | 'rtl' => false,
71 |
72 | // Fonts used to render text.
73 | // If contains more than one fonts, randomly selected based on name supplied
74 | 'fonts' => [__DIR__.'/../fonts/OpenSans-Bold.ttf', __DIR__.'/../fonts/rockwell.ttf'],
75 |
76 | // List of foreground colors to be used, randomly selected based on name supplied
77 | 'foregrounds' => [
78 | '#FFFFFF',
79 | ],
80 |
81 | // List of background colors to be used, randomly selected based on name supplied
82 | 'backgrounds' => [
83 | '#f44336',
84 | '#E91E63',
85 | '#9C27B0',
86 | '#673AB7',
87 | '#3F51B5',
88 | '#2196F3',
89 | '#03A9F4',
90 | '#00BCD4',
91 | '#009688',
92 | '#4CAF50',
93 | '#8BC34A',
94 | '#CDDC39',
95 | '#FFC107',
96 | '#FF9800',
97 | '#FF5722',
98 | ],
99 |
100 | 'border' => [
101 | 'size' => 1,
102 |
103 | // border color, available value are:
104 | // 'foreground' (same as foreground color)
105 | // 'background' (same as background color)
106 | // or any valid hex ('#aabbcc')
107 | 'color' => 'background',
108 |
109 | // border radius, currently only work for SVG
110 | 'radius' => 0,
111 | ],
112 |
113 | // List of theme name to be used when rendering avatar
114 | // Possible values are:
115 | // 1. Theme name as string: 'colorful'
116 | // 2. Or array of string name: ['grayscale-light', 'grayscale-dark']
117 | // 3. Or wildcard "*" to use all defined themes
118 | 'theme' => ['colorful'],
119 |
120 | // Predefined themes
121 | // Available theme attributes are:
122 | // shape, chars, backgrounds, foregrounds, fonts, fontSize, width, height, ascii, uppercase, and border.
123 | 'themes' => [
124 | 'grayscale-light' => [
125 | 'backgrounds' => ['#edf2f7', '#e2e8f0', '#cbd5e0'],
126 | 'foregrounds' => ['#a0aec0'],
127 | ],
128 | 'grayscale-dark' => [
129 | 'backgrounds' => ['#2d3748', '#4a5568', '#718096'],
130 | 'foregrounds' => ['#e2e8f0'],
131 | ],
132 | 'colorful' => [
133 | 'backgrounds' => [
134 | '#f44336',
135 | '#E91E63',
136 | '#9C27B0',
137 | '#673AB7',
138 | '#3F51B5',
139 | '#2196F3',
140 | '#03A9F4',
141 | '#00BCD4',
142 | '#009688',
143 | '#4CAF50',
144 | '#8BC34A',
145 | '#CDDC39',
146 | '#FFC107',
147 | '#FF9800',
148 | '#FF5722',
149 | ],
150 | 'foregrounds' => ['#FFFFFF'],
151 | ],
152 | 'pastel' => [
153 | 'backgrounds' => [
154 | '#ef9a9a',
155 | '#F48FB1',
156 | '#CE93D8',
157 | '#B39DDB',
158 | '#9FA8DA',
159 | '#90CAF9',
160 | '#81D4FA',
161 | '#80DEEA',
162 | '#80CBC4',
163 | '#A5D6A7',
164 | '#E6EE9C',
165 | '#FFAB91',
166 | '#FFCCBC',
167 | '#D7CCC8',
168 | ],
169 | 'foregrounds' => [
170 | '#FFF',
171 | ],
172 | ],
173 | ],
174 | ];
175 |
--------------------------------------------------------------------------------
/config/config-hd.php:
--------------------------------------------------------------------------------
1 | [
22 | // Enable HD mode - when true, uses higher resolution settings
23 | 'enabled' => env('AVATAR_HD_ENABLED', true),
24 |
25 | // HD dimensions (default: 512x512, supports up to 2048x2048)
26 | 'width' => env('AVATAR_HD_WIDTH', 512),
27 | 'height' => env('AVATAR_HD_HEIGHT', 512),
28 |
29 | // HD font size (scales with dimensions)
30 | 'fontSize' => env('AVATAR_HD_FONT_SIZE', 192),
31 |
32 | // Export quality settings
33 | 'quality' => [
34 | 'png' => env('AVATAR_HD_PNG_QUALITY', 95),
35 | 'jpg' => env('AVATAR_HD_JPG_QUALITY', 90),
36 | 'webp' => env('AVATAR_HD_WEBP_QUALITY', 85),
37 | ],
38 |
39 | // Anti-aliasing for smoother edges
40 | 'antialiasing' => env('AVATAR_HD_ANTIALIASING', true),
41 |
42 | // DPI for high-quality rendering
43 | 'dpi' => env('AVATAR_HD_DPI', 300),
44 | ],
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Export and Storage Settings
49 | |--------------------------------------------------------------------------
50 | | Configuration for image export and storage optimization
51 | |
52 | */
53 | 'export' => [
54 | // Default export format
55 | 'format' => env('AVATAR_EXPORT_FORMAT', 'png'), // png, jpg, webp
56 |
57 | // Export path (relative to storage/app)
58 | 'path' => env('AVATAR_EXPORT_PATH', 'avatars'),
59 |
60 | // Filename pattern: {name}, {initials}, {hash}, {timestamp}
61 | 'filename_pattern' => env('AVATAR_EXPORT_FILENAME', '{hash}_{timestamp}.{format}'),
62 |
63 | // Enable multiple format export
64 | 'multiple_formats' => env('AVATAR_EXPORT_MULTIPLE', false),
65 |
66 | // Progressive JPEG for better loading
67 | 'progressive_jpeg' => env('AVATAR_PROGRESSIVE_JPEG', true),
68 |
69 | // WebP lossless compression
70 | 'webp_lossless' => env('AVATAR_WEBP_LOSSLESS', false),
71 | ],
72 |
73 | /*
74 | |--------------------------------------------------------------------------
75 | | Performance and Caching
76 | |--------------------------------------------------------------------------
77 | | Enhanced caching and performance settings for HD avatars
78 | |
79 | */
80 | 'performance' => [
81 | // Enable file-based caching in addition to memory cache
82 | 'file_cache' => env('AVATAR_FILE_CACHE', true),
83 |
84 | // Cache different sizes separately
85 | 'size_based_cache' => env('AVATAR_SIZE_CACHE', true),
86 |
87 | // Preload fonts for better performance
88 | 'preload_fonts' => env('AVATAR_PRELOAD_FONTS', true),
89 |
90 | // Background processing for large batches
91 | 'background_processing' => env('AVATAR_BACKGROUND_PROCESSING', false),
92 |
93 | // Lazy loading support
94 | 'lazy_loading' => env('AVATAR_LAZY_LOADING', true),
95 |
96 | // Compression levels
97 | 'compression' => [
98 | 'png' => env('AVATAR_PNG_COMPRESSION', 6), // 0-9
99 | 'webp' => env('AVATAR_WEBP_COMPRESSION', 80), // 0-100
100 | ],
101 | ],
102 |
103 | /*
104 | |--------------------------------------------------------------------------
105 | | Storage Management
106 | |--------------------------------------------------------------------------
107 | | Configuration for storage optimization and cleanup
108 | |
109 | */
110 | 'storage' => [
111 | // Automatic cleanup of old files
112 | 'auto_cleanup' => env('AVATAR_AUTO_CLEANUP', true),
113 |
114 | // Maximum age for cached files (in days)
115 | 'max_age_days' => env('AVATAR_MAX_AGE_DAYS', 30),
116 |
117 | // Maximum storage size (in MB, 0 = unlimited)
118 | 'max_storage_mb' => env('AVATAR_MAX_STORAGE_MB', 500),
119 |
120 | // Storage driver (local, s3, etc.)
121 | 'disk' => env('AVATAR_STORAGE_DISK', 'local'),
122 |
123 | // CDN URL for serving images
124 | 'cdn_url' => env('AVATAR_CDN_URL', null),
125 |
126 | // Enable storage metrics
127 | 'metrics' => env('AVATAR_STORAGE_METRICS', false),
128 | ],
129 |
130 | /*
131 | |--------------------------------------------------------------------------
132 | | HD Themes
133 | |--------------------------------------------------------------------------
134 | | Enhanced themes with HD-specific optimizations
135 | |
136 | */
137 | 'hd_themes' => [
138 | 'ultra-hd' => [
139 | 'width' => 1024,
140 | 'height' => 1024,
141 | 'fontSize' => 384,
142 | 'backgrounds' => [
143 | '#667eea', '#764ba2', '#f093fb', '#f5576c',
144 | '#4facfe', '#00f2fe', '#43e97b', '#38f9d7',
145 | '#ffecd2', '#fcb69f', '#a8edea', '#fed6e3',
146 | ],
147 | 'foregrounds' => ['#FFFFFF'],
148 | 'border' => [
149 | 'size' => 4,
150 | 'color' => 'foreground',
151 | 'radius' => 8,
152 | ],
153 | ],
154 | 'retina' => [
155 | 'width' => 512,
156 | 'height' => 512,
157 | 'fontSize' => 192,
158 | 'backgrounds' => [
159 | '#667eea', '#764ba2', '#f093fb', '#f5576c',
160 | '#4facfe', '#00f2fe', '#43e97b', '#38f9d7',
161 | ],
162 | 'foregrounds' => ['#FFFFFF'],
163 | ],
164 | 'material-hd' => [
165 | 'width' => 384,
166 | 'height' => 384,
167 | 'fontSize' => 144,
168 | 'shape' => 'circle',
169 | 'backgrounds' => [
170 | '#1976D2', '#388E3C', '#F57C00', '#7B1FA2',
171 | '#5D4037', '#455A64', '#E64A19', '#00796B',
172 | ],
173 | 'foregrounds' => ['#FFFFFF'],
174 | 'border' => [
175 | 'size' => 2,
176 | 'color' => 'background',
177 | 'radius' => 0,
178 | ],
179 | ],
180 | ],
181 |
182 | /*
183 | |--------------------------------------------------------------------------
184 | | Responsive Sizes
185 | |--------------------------------------------------------------------------
186 | | Predefined sizes for responsive avatar generation
187 | |
188 | */
189 | 'responsive_sizes' => [
190 | 'thumbnail' => ['width' => 64, 'height' => 64, 'fontSize' => 24],
191 | 'small' => ['width' => 128, 'height' => 128, 'fontSize' => 48],
192 | 'medium' => ['width' => 256, 'height' => 256, 'fontSize' => 96],
193 | 'large' => ['width' => 512, 'height' => 512, 'fontSize' => 192],
194 | 'xl' => ['width' => 768, 'height' => 768, 'fontSize' => 288],
195 | 'xxl' => ['width' => 1024, 'height' => 1024, 'fontSize' => 384],
196 | ],
197 |
198 | /*
199 | |--------------------------------------------------------------------------
200 | | Advanced Features
201 | |--------------------------------------------------------------------------
202 | | Additional HD avatar features
203 | |
204 | */
205 | 'features' => [
206 | // Generate avatar sprites for animations
207 | 'sprites' => env('AVATAR_SPRITES', false),
208 |
209 | // Generate avatar variations (different colors/styles)
210 | 'variations' => env('AVATAR_VARIATIONS', false),
211 |
212 | // Generate blur placeholder images
213 | 'placeholders' => env('AVATAR_PLACEHOLDERS', true),
214 |
215 | // Generate different aspect ratios
216 | 'aspect_ratios' => env('AVATAR_ASPECT_RATIOS', false),
217 |
218 | // Watermarking support
219 | 'watermark' => [
220 | 'enabled' => env('AVATAR_WATERMARK', false),
221 | 'text' => env('AVATAR_WATERMARK_TEXT', ''),
222 | 'opacity' => env('AVATAR_WATERMARK_OPACITY', 0.3),
223 | 'position' => env('AVATAR_WATERMARK_POSITION', 'bottom-right'),
224 | ],
225 | ],
226 |
227 | /*
228 | |--------------------------------------------------------------------------
229 | | API Settings
230 | |--------------------------------------------------------------------------
231 | | Configuration for avatar API endpoints
232 | |
233 | */
234 | 'api' => [
235 | // Enable avatar API endpoints
236 | 'enabled' => env('AVATAR_API_ENABLED', true),
237 |
238 | // Rate limiting (requests per minute)
239 | 'rate_limit' => env('AVATAR_API_RATE_LIMIT', 60),
240 |
241 | // Enable CORS for API endpoints
242 | 'cors' => env('AVATAR_API_CORS', true),
243 |
244 | // API authentication
245 | 'auth' => env('AVATAR_API_AUTH', false),
246 |
247 | // Response headers
248 | 'headers' => [
249 | 'Cache-Control' => 'public, max-age=31536000', // 1 year
250 | 'Expires' => gmdate('D, d M Y H:i:s', time() + 31536000).' GMT',
251 | ],
252 | ],
253 | ];
254 |
--------------------------------------------------------------------------------
/config/hd-avatar.php:
--------------------------------------------------------------------------------
1 | [
22 | // Enable HD mode - when true, uses higher resolution settings
23 | 'enabled' => env('AVATAR_HD_ENABLED', true),
24 |
25 | // HD dimensions (default: 512x512, supports up to 2048x2048)
26 | 'width' => env('AVATAR_HD_WIDTH', 512),
27 | 'height' => env('AVATAR_HD_HEIGHT', 512),
28 |
29 | // HD font size (scales with dimensions)
30 | 'fontSize' => env('AVATAR_HD_FONT_SIZE', 192),
31 |
32 | // Export quality settings
33 | 'quality' => [
34 | 'png' => env('AVATAR_HD_PNG_QUALITY', 95),
35 | 'jpg' => env('AVATAR_HD_JPG_QUALITY', 90),
36 | 'webp' => env('AVATAR_HD_WEBP_QUALITY', 85),
37 | ],
38 |
39 | // Anti-aliasing for smoother edges
40 | 'antialiasing' => env('AVATAR_HD_ANTIALIASING', true),
41 |
42 | // DPI for high-quality rendering
43 | 'dpi' => env('AVATAR_HD_DPI', 300),
44 | ],
45 |
46 | /*
47 | |--------------------------------------------------------------------------
48 | | Export and Storage Settings
49 | |--------------------------------------------------------------------------
50 | | Configuration for image export and storage optimization
51 | |
52 | */
53 | 'export' => [
54 | // Default export format
55 | 'format' => env('AVATAR_EXPORT_FORMAT', 'png'), // png, jpg, webp
56 |
57 | // Export path (relative to storage/app)
58 | 'path' => env('AVATAR_EXPORT_PATH', 'avatars'),
59 |
60 | // Filename pattern: {name}, {initials}, {hash}, {timestamp}
61 | 'filename_pattern' => env('AVATAR_EXPORT_FILENAME', '{hash}_{timestamp}.{format}'),
62 |
63 | // Enable multiple format export
64 | 'multiple_formats' => env('AVATAR_EXPORT_MULTIPLE', false),
65 |
66 | // Progressive JPEG for better loading
67 | 'progressive_jpeg' => env('AVATAR_PROGRESSIVE_JPEG', true),
68 |
69 | // WebP lossless compression
70 | 'webp_lossless' => env('AVATAR_WEBP_LOSSLESS', false),
71 | ],
72 |
73 | /*
74 | |--------------------------------------------------------------------------
75 | | Performance and Caching
76 | |--------------------------------------------------------------------------
77 | | Enhanced caching and performance settings for HD avatars
78 | |
79 | */
80 | 'performance' => [
81 | // Enable file-based caching in addition to memory cache
82 | 'file_cache' => env('AVATAR_FILE_CACHE', true),
83 |
84 | // Cache different sizes separately
85 | 'size_based_cache' => env('AVATAR_SIZE_CACHE', true),
86 |
87 | // Preload fonts for better performance
88 | 'preload_fonts' => env('AVATAR_PRELOAD_FONTS', true),
89 |
90 | // Background processing for large batches
91 | 'background_processing' => env('AVATAR_BACKGROUND_PROCESSING', false),
92 |
93 | // Lazy loading support
94 | 'lazy_loading' => env('AVATAR_LAZY_LOADING', true),
95 |
96 | // Compression levels
97 | 'compression' => [
98 | 'png' => env('AVATAR_PNG_COMPRESSION', 6), // 0-9
99 | 'webp' => env('AVATAR_WEBP_COMPRESSION', 80), // 0-100
100 | ],
101 | ],
102 |
103 | /*
104 | |--------------------------------------------------------------------------
105 | | Storage Management
106 | |--------------------------------------------------------------------------
107 | | Configuration for storage optimization and cleanup
108 | |
109 | */
110 | 'storage' => [
111 | // Automatic cleanup of old files
112 | 'auto_cleanup' => env('AVATAR_AUTO_CLEANUP', true),
113 |
114 | // Maximum age for cached files (in days)
115 | 'max_age_days' => env('AVATAR_MAX_AGE_DAYS', 30),
116 |
117 | // Maximum storage size (in MB, 0 = unlimited)
118 | 'max_storage_mb' => env('AVATAR_MAX_STORAGE_MB', 500),
119 |
120 | // Storage driver (local, s3, etc.)
121 | 'disk' => env('AVATAR_STORAGE_DISK', 'local'),
122 |
123 | // CDN URL for serving images
124 | 'cdn_url' => env('AVATAR_CDN_URL', null),
125 |
126 | // Enable storage metrics
127 | 'metrics' => env('AVATAR_STORAGE_METRICS', false),
128 | ],
129 |
130 | /*
131 | |--------------------------------------------------------------------------
132 | | HD Themes
133 | |--------------------------------------------------------------------------
134 | | Enhanced themes with HD-specific optimizations
135 | |
136 | */
137 | 'hd_themes' => [
138 | 'ultra-hd' => [
139 | 'width' => 1024,
140 | 'height' => 1024,
141 | 'fontSize' => 384,
142 | 'backgrounds' => [
143 | '#667eea', '#764ba2', '#f093fb', '#f5576c',
144 | '#4facfe', '#00f2fe', '#43e97b', '#38f9d7',
145 | '#ffecd2', '#fcb69f', '#a8edea', '#fed6e3',
146 | ],
147 | 'foregrounds' => ['#FFFFFF'],
148 | 'border' => [
149 | 'size' => 4,
150 | 'color' => 'foreground',
151 | 'radius' => 8,
152 | ],
153 | ],
154 | 'retina' => [
155 | 'width' => 512,
156 | 'height' => 512,
157 | 'fontSize' => 192,
158 | 'backgrounds' => [
159 | '#667eea', '#764ba2', '#f093fb', '#f5576c',
160 | '#4facfe', '#00f2fe', '#43e97b', '#38f9d7',
161 | ],
162 | 'foregrounds' => ['#FFFFFF'],
163 | ],
164 | 'material-hd' => [
165 | 'width' => 384,
166 | 'height' => 384,
167 | 'fontSize' => 144,
168 | 'shape' => 'circle',
169 | 'backgrounds' => [
170 | '#1976D2', '#388E3C', '#F57C00', '#7B1FA2',
171 | '#5D4037', '#455A64', '#E64A19', '#00796B',
172 | ],
173 | 'foregrounds' => ['#FFFFFF'],
174 | 'border' => [
175 | 'size' => 2,
176 | 'color' => 'background',
177 | 'radius' => 0,
178 | ],
179 | ],
180 | ],
181 |
182 | /*
183 | |--------------------------------------------------------------------------
184 | | Responsive Sizes
185 | |--------------------------------------------------------------------------
186 | | Predefined sizes for responsive avatar generation
187 | |
188 | */
189 | 'responsive_sizes' => [
190 | 'thumbnail' => ['width' => 64, 'height' => 64, 'fontSize' => 24],
191 | 'small' => ['width' => 128, 'height' => 128, 'fontSize' => 48],
192 | 'medium' => ['width' => 256, 'height' => 256, 'fontSize' => 96],
193 | 'large' => ['width' => 512, 'height' => 512, 'fontSize' => 192],
194 | 'xl' => ['width' => 768, 'height' => 768, 'fontSize' => 288],
195 | 'xxl' => ['width' => 1024, 'height' => 1024, 'fontSize' => 384],
196 | ],
197 |
198 | /*
199 | |--------------------------------------------------------------------------
200 | | Advanced Features
201 | |--------------------------------------------------------------------------
202 | | Additional HD avatar features
203 | |
204 | */
205 | 'features' => [
206 | // Generate avatar sprites for animations
207 | 'sprites' => env('AVATAR_SPRITES', false),
208 |
209 | // Generate avatar variations (different colors/styles)
210 | 'variations' => env('AVATAR_VARIATIONS', false),
211 |
212 | // Generate blur placeholder images
213 | 'placeholders' => env('AVATAR_PLACEHOLDERS', true),
214 |
215 | // Generate different aspect ratios
216 | 'aspect_ratios' => env('AVATAR_ASPECT_RATIOS', false),
217 |
218 | // Watermarking support
219 | 'watermark' => [
220 | 'enabled' => env('AVATAR_WATERMARK', false),
221 | 'text' => env('AVATAR_WATERMARK_TEXT', ''),
222 | 'opacity' => env('AVATAR_WATERMARK_OPACITY', 0.3),
223 | 'position' => env('AVATAR_WATERMARK_POSITION', 'bottom-right'),
224 | ],
225 | ],
226 |
227 | /*
228 | |--------------------------------------------------------------------------
229 | | API Settings
230 | |--------------------------------------------------------------------------
231 | | Configuration for avatar API endpoints
232 | |
233 | */
234 | 'api' => [
235 | // Enable avatar API endpoints
236 | 'enabled' => env('AVATAR_API_ENABLED', true),
237 |
238 | // Rate limiting (requests per minute)
239 | 'rate_limit' => env('AVATAR_API_RATE_LIMIT', 60),
240 |
241 | // Enable CORS for API endpoints
242 | 'cors' => env('AVATAR_API_CORS', true),
243 |
244 | // API authentication
245 | 'auth' => env('AVATAR_API_AUTH', false),
246 |
247 | // Response headers
248 | 'headers' => [
249 | 'Cache-Control' => 'public, max-age=31536000', // 1 year
250 | 'Expires' => gmdate('D, d M Y H:i:s', time() + 31536000) . ' GMT',
251 | ],
252 | ],
253 | ];
254 |
--------------------------------------------------------------------------------
/examples/hd-avatar-examples.php:
--------------------------------------------------------------------------------
1 | createAndExport('John Doe', 'png');
28 | echo 'Avatar URL: '.$result['url']."\n";
29 | echo 'Avatar Hash: '.$result['metadata']['hash']."\n";
30 | echo "Dimensions: {$result['metadata']['width']}x{$result['metadata']['height']}\n\n";
31 |
32 | // =============================================================================
33 | // Responsive Avatar Generation
34 | // =============================================================================
35 |
36 | echo "=== Responsive Avatar Generation ===\n";
37 | // Create avatar with multiple sizes
38 | $responsiveResult = $hdAvatar->createAndExport('Jane Smith', 'webp');
39 | foreach ($responsiveResult['responsive_urls'] as $size => $url) {
40 | echo "Size {$size}: {$url}\n";
41 | }
42 | echo "\n";
43 |
44 | // =============================================================================
45 | // Batch Avatar Generation
46 | // =============================================================================
47 |
48 | echo "=== Batch Avatar Generation ===\n";
49 | $names = ['Alice Johnson', 'Bob Wilson', 'Carol Brown', 'David Miller'];
50 | $batchResult = $hdAvatar->batchCreateAndExport($names, 'png');
51 |
52 | echo "Processed {$batchResult['batch_info']['total_count']} avatars in {$batchResult['batch_info']['processing_time_seconds']} seconds\n";
53 | echo "Average time per avatar: {$batchResult['batch_info']['average_time_per_avatar']} seconds\n\n";
54 |
55 | // =============================================================================
56 | // Multiple Format Export
57 | // =============================================================================
58 |
59 | echo "=== Multiple Format Export ===\n";
60 | $allFormats = $hdAvatar->exportAllFormats('Emma Davis');
61 | foreach ($allFormats as $format => $urls) {
62 | echo "Format {$format}: {$urls['url']}\n";
63 | }
64 | echo "\n";
65 |
66 | // =============================================================================
67 | // Advanced HD Avatar with Custom Settings
68 | // =============================================================================
69 |
70 | echo "=== Advanced HD Avatar with Custom Settings ===\n";
71 |
72 | // Create HD avatar with custom configuration
73 | $customHDAvatar = new HDAvatarResponse([
74 | 'hd' => [
75 | 'enabled' => true,
76 | 'width' => 1024,
77 | 'height' => 1024,
78 | 'fontSize' => 384,
79 | 'quality' => [
80 | 'png' => 100,
81 | 'jpg' => 95,
82 | 'webp' => 90,
83 | ],
84 | ],
85 | 'responsive_sizes' => [
86 | 'thumbnail' => ['width' => 64, 'height' => 64, 'fontSize' => 24],
87 | 'small' => ['width' => 128, 'height' => 128, 'fontSize' => 48],
88 | 'medium' => ['width' => 256, 'height' => 256, 'fontSize' => 96],
89 | 'large' => ['width' => 512, 'height' => 512, 'fontSize' => 192],
90 | 'ultra' => ['width' => 1024, 'height' => 1024, 'fontSize' => 384],
91 | ],
92 | ]);
93 |
94 | // Create ultra-HD avatar
95 | $ultraHD = $customHDAvatar->createHD('Michael Chen')
96 | ->setDimension(1024, 1024)
97 | ->setFontSize(384)
98 | ->setBackground('#667eea')
99 | ->setForeground('#FFFFFF');
100 |
101 | // Export in multiple formats
102 | $ultraFiles = $ultraHD->exportMultiple(['png', 'jpg', 'webp']);
103 | foreach ($ultraFiles as $format => $file) {
104 | echo "Ultra-HD {$format}: {$file}\n";
105 | }
106 | echo "\n";
107 |
108 | // =============================================================================
109 | // Performance Optimization Examples
110 | // =============================================================================
111 |
112 | echo "=== Performance Optimization ===\n";
113 |
114 | // Configure storage optimization
115 | $hdAvatar->configureStorage('local', 'optimized-avatars', 1000); // 1GB limit
116 | $hdAvatar->setCompressionEnabled(true);
117 | $hdAvatar->setMaxFileAge(14); // 14 days
118 |
119 | // Get storage statistics
120 | $stats = $hdAvatar->getStorageStatistics();
121 | echo "Storage Usage: {$stats['total_size_mb']} MB ({$stats['usage_percentage']}%)\n";
122 | echo "Total Files: {$stats['total_files']}\n";
123 |
124 | // Optimize storage if needed
125 | if ($stats['usage_percentage'] > 80) {
126 | echo "Optimizing storage...\n";
127 | $optimization = $hdAvatar->optimizeStorage();
128 | echo "Removed {$optimization['optimization_summary']['files_removed']} files\n";
129 | echo "Saved {$optimization['optimization_summary']['space_saved_mb']} MB\n";
130 | }
131 | echo "\n";
132 |
133 | // =============================================================================
134 | // API Response Generation
135 | // =============================================================================
136 |
137 | echo "=== API Response Generation ===\n";
138 |
139 | // Generate API-ready response
140 | $apiResponse = $hdAvatar->apiResponse('Sarah Wilson', 'webp', 'large');
141 | echo "API Response for {$apiResponse['data']['avatar']['name']}:\n";
142 | echo "- URL: {$apiResponse['data']['avatar']['url']}\n";
143 | echo "- Initials: {$apiResponse['data']['avatar']['initials']}\n";
144 | echo "- Hash: {$apiResponse['data']['metadata']['hash']}\n";
145 | echo "- Cache Key: {$apiResponse['data']['metadata']['cache_key']}\n";
146 | echo "\n";
147 |
148 | // =============================================================================
149 | // Watermarked Avatars
150 | // =============================================================================
151 |
152 | echo "=== Watermarked Avatars ===\n";
153 |
154 | // Create avatar with watermark using the ImageExport trait
155 | $watermarkedPath = 'storage/avatars/watermarked_avatar.png';
156 | $hdAvatar->createHD('Company User')
157 | ->setBackground('#4f46e5')
158 | ->setForeground('#ffffff');
159 |
160 | // Export with watermark (this would work in a real Laravel environment)
161 | echo "Watermarked avatar would be saved to: {$watermarkedPath}\n\n";
162 |
163 | // =============================================================================
164 | // Health Check and Monitoring
165 | // =============================================================================
166 |
167 | echo "=== Health Check ===\n";
168 |
169 | $health = $hdAvatar->healthCheck();
170 | echo "System Status: {$health['status']}\n";
171 |
172 | foreach ($health['checks'] as $check => $status) {
173 | $statusText = $status ? 'PASS' : 'FAIL';
174 | echo "- {$check}: {$statusText}\n";
175 | }
176 |
177 | if (! empty($health['warnings'])) {
178 | echo "Warnings:\n";
179 | foreach ($health['warnings'] as $warning) {
180 | echo "- {$warning}\n";
181 | }
182 | }
183 | echo "\n";
184 |
185 | // =============================================================================
186 | // Avatar Information Retrieval
187 | // =============================================================================
188 |
189 | echo "=== Avatar Information ===\n";
190 |
191 | $avatarInfo = $hdAvatar->getAvatarInfo('Technical User');
192 | echo "Avatar Details:\n";
193 | echo "- Name: {$avatarInfo['name']}\n";
194 | echo "- Initials: {$avatarInfo['initials']}\n";
195 | echo "- Dimensions: {$avatarInfo['dimensions']['width']}x{$avatarInfo['dimensions']['height']}\n";
196 | echo "- Background: {$avatarInfo['styling']['background']}\n";
197 | echo "- Foreground: {$avatarInfo['styling']['foreground']}\n";
198 | echo '- HD Enabled: '.($avatarInfo['configuration']['hd_enabled'] ? 'Yes' : 'No')."\n";
199 | echo '- Cache Enabled: '.($avatarInfo['performance']['cache_enabled'] ? 'Yes' : 'No')."\n";
200 |
201 | echo "\nEstimated File Sizes:\n";
202 | foreach ($avatarInfo['estimated_file_sizes'] as $format => $size) {
203 | echo "- {$format}: {$size}\n";
204 | }
205 | echo "\n";
206 |
207 | // =============================================================================
208 | // Placeholder Generation for Lazy Loading
209 | // =============================================================================
210 |
211 | echo "=== Placeholder Generation ===\n";
212 |
213 | $placeholder = $hdAvatar->createHD('Lazy User')->toPlaceholder(32, 32);
214 | echo 'Placeholder Data URI: '.substr($placeholder, 0, 50)."...\n";
215 | echo 'Placeholder Length: '.strlen($placeholder)." characters\n\n";
216 |
217 | // =============================================================================
218 | // Sprite Sheet Generation
219 | // =============================================================================
220 |
221 | echo "=== Sprite Sheet Generation ===\n";
222 |
223 | $variations = [
224 | ['background' => '#ff6b6b', 'foreground' => '#ffffff'],
225 | ['background' => '#4ecdc4', 'foreground' => '#ffffff'],
226 | ['background' => '#45b7d1', 'foreground' => '#ffffff'],
227 | ['background' => '#96ceb4', 'foreground' => '#ffffff'],
228 | ['background' => '#ffeaa7', 'foreground' => '#2d3436'],
229 | ];
230 |
231 | $spriteUrl = $hdAvatar->createSpriteSheet('Sprite User', $variations, 'png');
232 | echo "Sprite sheet created: {$spriteUrl}\n\n";
233 |
234 | echo "=== HD Avatar Examples Complete ===\n";
235 | echo "All examples have been demonstrated successfully!\n";
236 | echo "Remember to configure your Laravel storage and cache settings for optimal performance.\n";
237 |
--------------------------------------------------------------------------------
/src/HDAvatar.php:
--------------------------------------------------------------------------------
1 | initializeStorage();
25 |
26 | // Configure storage settings from config
27 | if (isset($config['storage'])) {
28 | $storageConfig = $config['storage'];
29 | $this->configureStorage(
30 | $storageConfig['disk'] ?? 'local',
31 | $storageConfig['directory'] ?? 'avatars',
32 | $storageConfig['max_storage_mb'] ?? 500
33 | );
34 |
35 | $this->setMaxFileAge($storageConfig['max_age_days'] ?? 30);
36 | $this->setCompressionEnabled($storageConfig['compression'] ?? true);
37 | }
38 | }
39 |
40 | /**
41 | * Create and immediately export HD avatar
42 | */
43 | public function createAndExport(string $name, string $format = 'png', array $options = []): array
44 | {
45 | $this->createHD($name);
46 |
47 | $exported = [
48 | 'name' => $name,
49 | 'format' => $format,
50 | 'url' => $this->storeOptimized($name, $format, $options),
51 | 'responsive_urls' => [],
52 | 'metadata' => [
53 | 'width' => $this->width,
54 | 'height' => $this->height,
55 | 'font_size' => $this->fontSize,
56 | 'background' => $this->background,
57 | 'foreground' => $this->foreground,
58 | 'hash' => $this->generateContentHash(),
59 | ],
60 | ];
61 |
62 | // Generate responsive sizes if configured
63 | if (! empty($this->responsiveSizes)) {
64 | foreach ($this->responsiveSizes as $size => $dimensions) {
65 | $this->setDimension($dimensions['width'], $dimensions['height']);
66 | $this->setFontSize($dimensions['fontSize']);
67 |
68 | $exported['responsive_urls'][$size] = $this->storeOptimized("{$name}_{$size}", $format, $options);
69 | }
70 | }
71 |
72 | return $exported;
73 | }
74 |
75 | /**
76 | * Batch create and export multiple avatars
77 | */
78 | public function batchCreateAndExport(array $names, string $format = 'png', array $options = []): array
79 | {
80 | $results = [];
81 | $startTime = microtime(true);
82 |
83 | foreach ($names as $name) {
84 | $results[$name] = $this->createAndExport($name, $format, $options);
85 | }
86 |
87 | $processingTime = microtime(true) - $startTime;
88 |
89 | return [
90 | 'avatars' => $results,
91 | 'batch_info' => [
92 | 'total_count' => count($names),
93 | 'processing_time_seconds' => round($processingTime, 3),
94 | 'average_time_per_avatar' => round($processingTime / count($names), 3),
95 | 'format' => $format,
96 | 'timestamp' => now()->toISOString(),
97 | ],
98 | ];
99 | }
100 |
101 | /**
102 | * Generate avatar with all export formats
103 | */
104 | public function exportAllFormats(string $name, array $options = []): array
105 | {
106 | $this->createHD($name);
107 |
108 | $formats = ['png', 'jpg', 'webp'];
109 | $exports = [];
110 |
111 | foreach ($formats as $format) {
112 | $exports[$format] = [
113 | 'url' => $this->storeOptimized($name, $format, $options),
114 | 'cached_url' => $this->getCachedOrGenerate($name, $format, $options),
115 | ];
116 | }
117 |
118 | return $exports;
119 | }
120 |
121 | /**
122 | * Create avatar sprite sheet with variations
123 | */
124 | public function createSpriteSheet(string $name, array $variations, string $format = 'png'): string
125 | {
126 | $this->createHD($name);
127 |
128 | $filename = $this->generateOptimizedFilename("{$name}_sprite", $format);
129 | $path = $this->storageDirectory.'/'.$filename;
130 | $fullPath = Storage::disk($this->storageDisk)->path($path);
131 |
132 | $this->exportSpriteSheet($variations, $fullPath, $format);
133 |
134 | return Storage::disk($this->storageDisk)->url($path);
135 | }
136 |
137 | /**
138 | * Get comprehensive avatar information
139 | */
140 | public function getAvatarInfo(string $name): array
141 | {
142 | $this->createHD($name);
143 |
144 | return [
145 | 'name' => $name,
146 | 'initials' => $this->getInitial(),
147 | 'dimensions' => [
148 | 'width' => $this->width,
149 | 'height' => $this->height,
150 | ],
151 | 'styling' => [
152 | 'background' => $this->background,
153 | 'foreground' => $this->foreground,
154 | 'font_size' => $this->fontSize,
155 | 'shape' => $this->shape,
156 | 'border' => [
157 | 'size' => $this->borderSize,
158 | 'color' => $this->borderColor,
159 | 'radius' => $this->borderRadius,
160 | ],
161 | ],
162 | 'configuration' => [
163 | 'hd_enabled' => $this->hdEnabled,
164 | 'responsive_sizes' => $this->responsiveSizes,
165 | 'export_path' => $this->exportPath,
166 | ],
167 | 'performance' => [
168 | 'cache_enabled' => $this->cacheEnabled,
169 | 'compression_enabled' => $this->compressionEnabled,
170 | 'storage_disk' => $this->storageDisk,
171 | ],
172 | 'hash' => $this->generateContentHash(),
173 | 'estimated_file_sizes' => $this->estimateFileSizes(),
174 | ];
175 | }
176 |
177 | /**
178 | * Optimize existing avatar storage
179 | */
180 | public function optimizeStorage(): array
181 | {
182 | $beforeStats = $this->getStorageStatistics();
183 | $cleaned = $this->performCleanup();
184 | $afterStats = $this->getStorageStatistics();
185 |
186 | return [
187 | 'before' => $beforeStats,
188 | 'after' => $afterStats,
189 | 'cleaned' => $cleaned,
190 | 'optimization_summary' => [
191 | 'files_removed' => array_sum(array_map('count', $cleaned)),
192 | 'space_saved_mb' => round(($beforeStats['total_size_mb'] - $afterStats['total_size_mb']), 2),
193 | 'optimization_percentage' => round((($beforeStats['total_size_mb'] - $afterStats['total_size_mb']) / max($beforeStats['total_size_mb'], 0.1)) * 100, 2),
194 | ],
195 | ];
196 | }
197 |
198 | /**
199 | * Generate avatar API response
200 | */
201 | public function apiResponse(string $name, string $format = 'png', string $size = 'medium'): array
202 | {
203 | $info = $this->getAvatarInfo($name);
204 |
205 | // Set appropriate size
206 | if (isset($this->responsiveSizes[$size])) {
207 | $dimensions = $this->responsiveSizes[$size];
208 | $this->setDimension($dimensions['width'], $dimensions['height']);
209 | $this->setFontSize($dimensions['fontSize']);
210 | }
211 |
212 | return [
213 | 'success' => true,
214 | 'data' => [
215 | 'avatar' => [
216 | 'name' => $name,
217 | 'initials' => $info['initials'],
218 | 'url' => $this->getCachedOrGenerate($name, $format, []),
219 | 'placeholder' => $this->toPlaceholder(),
220 | 'responsive_urls' => $this->generateResponsiveUrls($name, $format),
221 | ],
222 | 'metadata' => [
223 | 'size' => $size,
224 | 'format' => $format,
225 | 'dimensions' => [
226 | 'width' => $this->width,
227 | 'height' => $this->height,
228 | ],
229 | 'hash' => $info['hash'],
230 | 'cache_key' => $this->generateCacheKey($name, $format, ['size' => $size]),
231 | ],
232 | ],
233 | 'timestamp' => now()->toISOString(),
234 | ];
235 | }
236 |
237 | /**
238 | * Generate responsive URLs for all configured sizes
239 | */
240 | protected function generateResponsiveUrls(string $name, string $format): array
241 | {
242 | $urls = [];
243 |
244 | foreach ($this->responsiveSizes as $size => $dimensions) {
245 | $urls[$size] = $this->getCachedUrl($format, $size);
246 | }
247 |
248 | return $urls;
249 | }
250 |
251 | /**
252 | * Health check for HD avatar system
253 | */
254 | public function healthCheck(): array
255 | {
256 | $stats = $this->getStorageStatistics();
257 | $exportStats = $this->getExportStats();
258 |
259 | $health = [
260 | 'status' => 'healthy',
261 | 'checks' => [
262 | 'storage_accessible' => true,
263 | 'cache_functional' => true,
264 | 'within_storage_limits' => $stats['usage_percentage'] < 90,
265 | 'export_formats_supported' => count($exportStats['supported_formats']) >= 3,
266 | ],
267 | 'warnings' => [],
268 | 'statistics' => [
269 | 'storage' => $stats,
270 | 'export' => $exportStats,
271 | ],
272 | ];
273 |
274 | // Add warnings based on checks
275 | if (! $health['checks']['within_storage_limits']) {
276 | $health['warnings'][] = "Storage usage is at {$stats['usage_percentage']}% - cleanup recommended";
277 | }
278 |
279 | if ($stats['total_files'] > 10000) {
280 | $health['warnings'][] = "Large number of cached files ({$stats['total_files']}) - consider cleanup";
281 | }
282 |
283 | // Overall status
284 | $failedChecks = array_filter($health['checks'], fn ($check) => ! $check);
285 | if (! empty($failedChecks)) {
286 | $health['status'] = 'degraded';
287 | }
288 |
289 | if (count($failedChecks) > 2) {
290 | $health['status'] = 'unhealthy';
291 | }
292 |
293 | return $health;
294 | }
295 | }
296 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # laravolt/avatar
2 |
3 | [](https://packagist.org/packages/laravolt/avatar)
4 | [](https://packagist.org/packages/laravolt/avatar)
5 | [](https://packagist.org/packages/laravolt/avatar)
6 | [](https://github.com/laravolt/avatar/workflows/run-tests/badge.svg)
7 |
8 | 
9 |
10 | Display unique avatar for any user based on their (initials) name.
11 |
12 | ## Preview
13 | 
14 | ## :film_strip: Video Tutorial
15 | [
](https://youtu.be/jD0wu88c5kw)
16 |
17 | ## Installation
18 | This package originally built for Laravel, but can also be used in any PHP project.
19 |
20 | [Read more about integration with PHP project here.](#integration-with-other-php-project)
21 |
22 | ### Laravel >= 5.2:
23 | ```bash
24 | composer require laravolt/avatar
25 | ```
26 |
27 | ### Laravel 5.1:
28 | ```bash
29 | composer require laravolt/avatar ~0.3
30 | ```
31 |
32 | ## Service Provider & Facade
33 | **Note: only for Laravel 5.4 and below, because since Laravel 5.5 we use package auto-discovery.**
34 |
35 | ```php
36 | Laravolt\Avatar\ServiceProvider::class,
37 |
38 | ...
39 |
40 | 'Avatar' => Laravolt\Avatar\Facade::class,
41 | ```
42 |
43 | ## Publish Config (optional)
44 | ```php
45 | php artisan vendor:publish --provider="Laravolt\Avatar\ServiceProvider"
46 | ```
47 | This will create config file located in `config/laravolt/avatar.php`.
48 |
49 | ## Lumen Service Provider
50 |
51 | ```php
52 | $app->register(Laravolt\Avatar\LumenServiceProvider);
53 | ```
54 |
55 | ## Usage
56 |
57 | ### Output as base64
58 | ```php
59 | //this will output data-uri (base64 image data)
60 | //something like data:image/png;base64,iVBORw0KGg....
61 | Avatar::create('Joko Widodo')->toBase64();
62 |
63 | //use in view
64 | //this will display initials JW as an image
65 |
66 | ```
67 |
68 | ### Save as file
69 | ```php
70 | Avatar::create('Susilo Bambang Yudhoyono')->save('sample.png');
71 | Avatar::create('Susilo Bambang Yudhoyono')->save('sample.jpg', 100); // quality = 100
72 | ```
73 |
74 | ### Output as Gravatar
75 | ```php
76 | Avatar::create('uyab@example.net')->toGravatar();
77 | // Output: http://gravatar.com/avatar/0c5cbf5a8762d91d930795a6107b2ce5814a6ab26e60c7ec6b75bc81c7dfe3ee
78 |
79 | Avatar::create('uyab@example.net')->toGravatar(['d' => 'identicon', 'r' => 'pg', 's' => 100]);
80 | // Output: http://gravatar.com/avatar/0c5cbf5a8762d91d930795a6107b2ce5814a6ab26e60c7ec6b75bc81c7dfe3ee?d=identicon&r=pg&s=100
81 | ```
82 | Gravatar parameter reference: https://docs.gravatar.com/api/avatars/images/
83 |
84 | ### Output as SVG
85 | ```php
86 | Avatar::create('Susilo Bambang Yudhoyono')->toSvg();
87 | ```
88 |
89 | You may specify custom font-family for your SVG text.
90 | ```html
91 |