├── .gitignore
├── .travis.yml
├── nbproject
├── project.xml
└── project.properties
├── phpunit.xml
├── favicon
├── composer.json
├── LICENSE
├── src
├── helpers.php
├── Html.php
├── Config.php
└── GenerateCommand.php
├── tests
└── HieuLe
│ └── Favicon
│ ├── ConfigTest.php
│ └── HtmlTest.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | composer.phar
2 | vendor/
3 | bin/
4 | composer.lock
5 | /nbproject/private/
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: php
2 |
3 | php:
4 | - 5.3
5 | - 5.4
6 | - 5.5
7 | - 5.6
8 | - hhvm
9 |
10 | before_script:
11 | - composer self-update
12 | - composer install --prefer-source --no-interaction --dev
13 |
14 | script: phpunit
--------------------------------------------------------------------------------
/nbproject/project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | org.netbeans.modules.php.project
4 |
5 |
6 | favicon
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 | ./tests/
16 |
17 |
18 |
--------------------------------------------------------------------------------
/favicon:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | add(new GenerateCommand);
34 | $application->run();
35 |
36 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hieu-le/favicon",
3 | "description": "A configurable PHP solution to auto generate favicons and HTML meta tags from a original PNG file.",
4 | "type": "library",
5 | "keywords": [
6 | "favicon",
7 | "generate",
8 | "png",
9 | "ico"
10 | ],
11 | "license": "MIT",
12 | "authors": [
13 | {
14 | "name": "Hieu Le",
15 | "email": "letrunghieu.cse09@gmail.com",
16 | "homepage": "http://www.hieule.info"
17 | }
18 | ],
19 | "require": {
20 | "php": ">=5.3.3",
21 | "imagine/imagine": "~0.1",
22 | "symfony/console": "~2.0"
23 | },
24 | "suggest": {
25 | "ext-imagick": "To use the binary and generate favicon images"
26 | },
27 | "bin": [
28 | "favicon"
29 | ],
30 | "config": {
31 | "bin-dir": "bin"
32 | },
33 | "autoload": {
34 | "psr-4": {
35 | "HieuLe\\Favicon\\": "src/"
36 | },
37 | "files": ["src/helpers.php"]
38 | },
39 | "require-dev": {
40 | "phpunit/phpunit": "~4.0"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Hieu Le Trung
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 |
23 |
--------------------------------------------------------------------------------
/src/helpers.php:
--------------------------------------------------------------------------------
1 | link and meta tags for favicons
28 | *
29 | * @param int $option decide which type of favicon will be included in the final result. Default will be FAVICON_ENABLE_ALL to include any thing. The old apple touch link tags will be exclude with FAVICON_NO_OLD_APPLE bit, the android manifest.json link tag will be exclude with FAVICON_NO_ANDROID bit and the Microsoft Windows and IE msapplication meta tags will be excluded with FAVICON_NO_MS bit.
30 | * @param array $msOptions more information for Windows and IE msapplication meta tags. The input array can have these field:
31 | *
32 | *
tile_color: the background of live tile when this site is pinned, default is white (#ffffff)
33 | *
browser_config_file: the path to browser config XML file if you have it. By default, it is set to an empty string to prevent IE from auto looking browserconfig.xml file
34 | *
application_name: the default application name displayed when user pinned this site
35 | *
36 | *
37 | * @return string the output HTML
38 | */
39 | function favicon($option = FAVICON_ENABLE_ALL, array $msOptions = array())
40 | {
41 | $noOldApple = FAVICON_NO_OLD_APPLE & $option;
42 | $noAndroid = FAVICON_NO_ANDROID & $option;
43 | $noMs = FAVICON_NO_MS & $option;
44 | $tileColor = strtoupper(isset($msOptions['tile_color']) ? $msOptions['tile_color'] : '#ffffff');
45 | $browserConfigFile = isset($msOptions['browser_config_file']) ? $msOptions['browser_config_file'] : '';
46 | $appName = isset($msOptions['application_name']) ? $msOptions['application_name'] : '';
47 | return HieuLe\Favicon\Html::output($noOldApple, $noAndroid, $noMs, $tileColor, $browserConfigFile, $appName);
48 | }
49 |
50 | }
51 |
52 |
--------------------------------------------------------------------------------
/tests/HieuLe/Favicon/ConfigTest.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class ConfigTest extends \PHPUnit_Framework_TestCase
11 | {
12 |
13 | public function testGetSizes()
14 | {
15 | $sizes = Config::getSizes();
16 | $this->assertCount(23, $sizes);
17 | }
18 |
19 | public function testGetSizesNoOldApple()
20 | {
21 | $sizes = Config::getSizes(true);
22 | $this->assertCount(19, $sizes);
23 | $this->assertArrayNotHasKey('apple-touch-icon-57x57.png', $sizes);
24 | $this->assertArrayNotHasKey('apple-touch-icon-60x60.png', $sizes);
25 | $this->assertArrayNotHasKey('apple-touch-icon-72x72.png', $sizes);
26 | $this->assertArrayNotHasKey('apple-touch-icon-114x114.png', $sizes);
27 | }
28 |
29 | public function testgetSizesNoAndroid()
30 | {
31 | $sizes = Config::getSizes(false, true);
32 | $this->assertCount(18, $sizes);
33 | $this->assertArrayNotHasKey('android-chrome-36x36.png', $sizes);
34 | $this->assertArrayNotHasKey('android-chrome-48x48.png', $sizes);
35 | $this->assertArrayNotHasKey('android-chrome-72x72.png', $sizes);
36 | $this->assertArrayNotHasKey('android-chrome-96x96.png', $sizes);
37 | $this->assertArrayNotHasKey('android-chrome-144x144.png', $sizes);
38 | }
39 |
40 | public function testGetSizesNoMs()
41 | {
42 | $sizes = Config::getSizes(false, true);
43 | $this->assertCount(18, $sizes);
44 | $this->assertArrayNotHasKey('mstile-70x70', $sizes);
45 | $this->assertArrayNotHasKey('mstile-144x144', $sizes);
46 | $this->assertArrayNotHasKey('mstile-150x150', $sizes);
47 | $this->assertArrayNotHasKey('mstile-310x310', $sizes);
48 | $this->assertArrayNotHasKey('mstile-310x150', $sizes);
49 | }
50 |
51 | public function testGetSizesNoAll()
52 | {
53 | $sizes = Config::getSizes(true, true, true);
54 | $this->assertCount(9, $sizes);
55 | }
56 |
57 | public function testGetTileSettings()
58 | {
59 | $opt = Config::getTileSettings('mstile-310x310.png');
60 | $this->assertCount(5, $opt);
61 | $this->assertArrayHasKey('w', $opt);
62 | $this->assertArrayHasKey('h', $opt);
63 | $this->assertArrayHasKey('icon', $opt);
64 | $this->assertArrayHasKey('top', $opt);
65 | $this->assertArrayHasKey('left', $opt);
66 | }
67 |
68 | /**
69 | * @expectedException \RuntimeException
70 | */
71 | public function testGetTileSettingsError()
72 | {
73 | $opt = Config::getTileSettings('mstile-300x300.png');
74 |
75 | }
76 |
77 | }
78 |
--------------------------------------------------------------------------------
/src/Html.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Html
11 | {
12 |
13 | /**
14 | * Write meta and link tags
15 | *
16 | * @param bool $noOldApple exclude old apple touch link
17 | * @param bool $noAndroid exclude android manifest.json file
18 | * @param bool $noMs exclude msapplication meta tags
19 | * @param tring $tileColor the color of Windows tile
20 | * @param string $browserConfigFile the path to browserconfig.xml file or null to disable this
21 | * @param string $appName the name of application when pinned
22 | *
23 | * @return string
24 | */
25 | public static function output($noOldApple = false, $noAndroid = false, $noMs = false, $tileColor = '#FFFFFF', $browserConfigFile = '', $appName = '')
26 | {
27 | $result = array();
28 | if (!$noMs)
29 | {
30 | if (!$browserConfigFile)
31 | {
32 | $result[] = '';
33 | }
34 | else
35 | {
36 | $result[] = '';
37 | }
38 | }
39 | if (!$noOldApple)
40 | {
41 | $result[] = '';
42 | $result[] = '';
43 | $result[] = '';
44 | $result[] = '';
45 | }
46 | $result[] = '';
47 | $result[] = '';
48 | $result[] = '';
49 | $result[] = '';
50 | $result[] = '';
51 | $result[] = '';
52 | $result[] = '';
53 | if (!$noAndroid)
54 | {
55 | $result[] = '';
56 | }
57 | if (!$noMs)
58 | {
59 | if ($appName)
60 | {
61 | $result[] = '';
62 | }
63 | $result[] = '';
64 | $result[] = '';
65 | $result[] = '';
66 | $result[] = '';
67 | $result[] = '';
68 | $result[] = '';
69 | }
70 |
71 | return implode("\n", $result);
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/nbproject/project.properties:
--------------------------------------------------------------------------------
1 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs=false
2 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width=4
3 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab=4
4 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.tab-size=4
5 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width=80
6 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap=none
7 | auxiliary.org-netbeans-modules-editor-indent.CodeStyle.usedProfile=project
8 | auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.expand-tabs=true
9 | auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.indent-shift-width=4
10 | auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.spaces-per-tab=4
11 | auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.tab-size=4
12 | auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.text-limit-width=80
13 | auxiliary.org-netbeans-modules-editor-indent.text.javascript.CodeStyle.project.text-line-wrap=none
14 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.alignMultilineCallArgs=true
15 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.alignMultilineImplements=true
16 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.alignMultilineMethodParams=true
17 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.catchBracePlacement=NEW_LINE
18 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.classDeclBracePlacement=NEW_LINE
19 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.expand-tabs=true
20 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.forBracePlacement=NEW_LINE
21 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.groupAlignmentArrayInit=true
22 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.groupAlignmentAssignment=true
23 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.ifBracePlacement=NEW_LINE
24 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.indent-shift-width=4
25 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.methodDeclBracePlacement=NEW_LINE
26 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeCatchOnNewLine=true
27 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeElseOnNewLine=true
28 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeFinallyOnNewLine=true
29 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.placeWhileOnNewLine=true
30 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.spaceAfterTypeCast=false
31 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.spaces-per-tab=4
32 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.switchBracePlacement=NEW_LINE
33 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.tab-size=4
34 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.text-limit-width=80
35 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.text-line-wrap=none
36 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.useTraitBodyBracePlacement=NEW_LINE
37 | auxiliary.org-netbeans-modules-editor-indent.text.x-php5.CodeStyle.project.whileBracePlacement=NEW_LINE
38 | include.path=${php.global.include.path}
39 | php.version=PHP_53
40 | source.encoding=UTF-8
41 | src.dir=.
42 | tags.asp=false
43 | tags.short=false
44 | web.root=.
45 |
--------------------------------------------------------------------------------
/src/Config.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class Config
11 | {
12 |
13 | /**
14 | * All available sizes
15 | *
16 | * @var array
17 | */
18 | private static $_sizes = array(
19 | 'favicon-16x16.png' => 16,
20 | 'favicon-32x32.png' => 32,
21 | 'favicon-96x96.png' => 96,
22 | 'android-chrome-36x36.png' => 36,
23 | 'android-chrome-48x48.png' => 48,
24 | 'android-chrome-72x72.png' => 72,
25 | 'android-chrome-96x96.png' => 96,
26 | 'android-chrome-144x144.png' => 144,
27 | 'android-chrome-192x192.png' => 192,
28 | 'mstile-70x70.png' => 70,
29 | 'mstile-144x144.png' => 144,
30 | 'mstile-150x150.png' => 150,
31 | 'mstile-310x310.png' => 310,
32 | 'mstile-310x150.png' => array(310, 150),
33 | 'apple-touch-icon.png' => 57,
34 | 'apple-touch-icon-57x57.png' => 57,
35 | 'apple-touch-icon-60x60.png' => 60,
36 | 'apple-touch-icon-72x72.png' => 72,
37 | 'apple-touch-icon-76x76.png' => 76,
38 | 'apple-touch-icon-114x114.png' => 114,
39 | 'apple-touch-icon-120x120.png' => 120,
40 | 'apple-touch-icon-152x152.png' => 152,
41 | 'apple-touch-icon-180x180.png' => 180,
42 | );
43 | private static $_tileSettings = array(
44 | 'mstile-70x70.png' => array(
45 | 'w' => 126,
46 | 'h' => 126,
47 | 'icon' => 100,
48 | 'top' => 13,
49 | 'left' => 13,
50 | ),
51 | 'mstile-150x150.png' => array(
52 | 'w' => 270,
53 | 'h' => 270,
54 | 'icon' => 124,
55 | 'top' => 73,
56 | 'left' => 50,
57 | ),
58 | 'mstile-310x310.png' => array(
59 | 'w' => 558,
60 | 'h' => 558,
61 | 'icon' => 260,
62 | 'top' => 149,
63 | 'left' => 128,
64 | ),
65 | 'mstile-310x150.png' => array(
66 | 'w' => 558,
67 | 'h' => 270,
68 | 'icon' => 130,
69 | 'top' => 214,
70 | 'left' => 45,
71 | ),
72 | );
73 |
74 | /**
75 | * Return sizes from options
76 | *
77 | * @param bool $noOldApple exclude old Apple touch image sizes
78 | * @param bool $noAndroid exclude manifest.json file and images for Androids
79 | * @param bool $noMs exclude images for Windows and IE11
80 | * @return array
81 | */
82 | public static function getSizes($noOldApple = false, $noAndroid = false, $noMs = false)
83 | {
84 | $result = array_merge(self::$_sizes, array());
85 | if ($noOldApple)
86 | {
87 | unset($result['apple-touch-icon-57x57.png']);
88 | unset($result['apple-touch-icon-60x60.png']);
89 | unset($result['apple-touch-icon-72x72.png']);
90 | unset($result['apple-touch-icon-114x114.png']);
91 | }
92 | if ($noAndroid)
93 | {
94 | unset($result['android-chrome-36x36.png']);
95 | unset($result['android-chrome-48x48.png']);
96 | unset($result['android-chrome-72x72.png']);
97 | unset($result['android-chrome-96x96.png']);
98 | unset($result['android-chrome-144x144.png']);
99 | }
100 | if ($noMs)
101 | {
102 | unset($result['mstile-70x70.png']);
103 | unset($result['mstile-144x144.png']);
104 | unset($result['mstile-150x150.png']);
105 | unset($result['mstile-310x310.png']);
106 | unset($result['mstile-310x150.png']);
107 | }
108 | return $result;
109 | }
110 |
111 | /**
112 | * Get the settings for Windows tile image size
113 | *
114 | * @param type $name
115 | * @return array
116 | * @throws \RuntimeException
117 | */
118 | public static function getTileSettings($name)
119 | {
120 | if (!isset(self::$_tileSettings[$name]))
121 | {
122 | throw new \RuntimeException('Invalid image name');
123 | }
124 | return self::$_tileSettings[$name];
125 | }
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # favicon
2 |
3 | [](https://travis-ci.org/letrunghieu/favicon) [](https://packagist.org/packages/hieu-le/favicon) [](https://packagist.org/packages/hieu-le/favicon) [](https://packagist.org/packages/hieu-le/favicon) [](https://packagist.org/packages/hieu-le/favicon)
4 |
5 | A configurable PHP solution to auto generate favicons and HTML tags from a original PNG file.
6 |
7 | ## What does this package do?
8 |
9 | In short, it will help you display correct favicon for your website with just **one** original PNG image.
10 |
11 | In more details, it supports:
12 |
13 | * create **one** ICO file and **many** PNG files with many favicon sizes from just **one** original PNG image as well as a `manifest.json` file for Android devices. Both input file path and output folder (which contains images and json files) are configurable via a command line interface.
14 | * Generate suitable `meta` and `link` tags for desktop web browsers as well as mobile touch devices to properly display favicon.
15 |
16 | ## Installation
17 |
18 | We need [PHP imagick extension](http://php.net/manual/en/book.imagick.php) or [PHP GD extension](http://php.net/manual/en/book.image.php) for generating images. By default, the Imagick extension is loaded, if you cannot install it, you can switch to using GD ~~via command line option~~ if available.
19 |
20 | You will need (Composer)[] to use this package. After install Composer, add this dependency into your `composer.json` file.
21 |
22 | ```
23 | "hieu-le/favicon" : "~1.0"
24 | ```
25 |
26 | Run `composer update` and start using.
27 |
28 | ## Generate images
29 |
30 | To use the command line to to generate favicon files:
31 |
32 | ```
33 | $ vendor/bin/favicon generate [--ico-64] [--ico-48] [--no-old-apple] [--no-android] [--no-ms] [--app-name="..."] input [output]
34 | ```
35 |
36 | Arguments:
37 |
38 | * `input`: path to the input image files, which is required
39 | * `output`: path to the folder which contains output files. If this folder does not exist, the package will try to create it. This argument is optional, default value is current folder.
40 |
41 | Options:
42 |
43 | * ~~`--use-gd`: use GD extension instead of Imagick extension~~ The Imagick ext is used by default. GD library is used if cannot load Imagick ext.
44 | * `--ico-64`: include the 64x64 image inside the output ICO file (which contains only 16x16 and 32x32 images by default)
45 | * `--ico-48`: include the 48x48 image inside the output ICO file (which contains only 16x16 and 32x32 images by default). Both `--ico-48` and `--ico-64` options make the output icon file larger a lot.
46 | * `--no-old-apple`: exclude pngs files that used by old Apple touch devices
47 | * `--no-android`: exclude `manifest.json` files and PNG files for Android devices
48 | * `--no-ms`: exclude images for Windows tile
49 | * `--app-name="..."` set the application name in the `manifest.json` file. Default is an empty string.
50 |
51 | ## Output HTML tags
52 |
53 | Call the `favicon` function inside your HTML template as follow:
54 |
55 | ```php
56 | echo favicon($option = FAVICON_ENABLE_ALL, array $msOptions = array())
57 | ```
58 |
59 | The `$option` argument is a bitmask with following bit:
60 |
61 | * `FAVICON_NO_OLD_APPLE` : do not include old apple touch `link` tags
62 | * `FAVICON_NO_ANDROID` : do not include Android `manifest.xml` link tag
63 | * `FAVICON_NO_MS` : do not include Windows and IE `meta` tags
64 |
65 | The default value is `FAVICON_ENABLE_ALL` turns of all these three bit and include everything in the final output. Here are some examples:
66 |
67 | * To exclude old apple touch `link` tags: `FAVICON_NO_OLD_APPLE`
68 | * To exclude Android manifest file and IE `meta` tags: `FAVICON_NO_ANDROID | FAVICON_NO_MS`
69 | * To exclude all these additional tags: `FAVICON_NO_OLD_APPLE | FAVICON_NO_ANDROID | FAVICON_NO_MS`
70 |
71 | The `$msOptions` argument is an array contains information for Windows and IE. It can has these fields:
72 |
73 | * `tile_color`: the background of live tile when this site is pinned, default is white (`#ffffff`)
74 | * `browser_config_file`: the path to browser config XML file if you have it. By default, it is set to an empty string to prevent IE from auto looking `browserconfig.xml` file
75 | * `application_name`: the default application name displayed when user pinned this site
76 |
77 | The result of
78 |
79 | ```php
80 | echo favicon(FAVICON_ENABLE_ALL, array(
81 | 'tile_color' => '#F0F0F0',
82 | 'browser_config_file' => 'IEConfig.xml',
83 | 'application_name' => 'Hieu Le Favicon'
84 | ));
85 | ```
86 | is a HTML segment link this:
87 |
88 | ```html
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | ```
110 |
111 |
112 | ## License
113 |
114 | The package is released under MIT license. See the [`LICENSE`](https://github.com/letrunghieu/favicon/blob/master/LICENSE) file for more detail.
115 |
--------------------------------------------------------------------------------
/src/GenerateCommand.php:
--------------------------------------------------------------------------------
1 |
15 | */
16 | class GenerateCommand extends Command
17 | {
18 |
19 | /**
20 | * Configuration object
21 | *
22 | * @var Config
23 | */
24 | private $_config;
25 |
26 | /**
27 | * Input file path
28 | *
29 | * @var string
30 | */
31 | private $_inputFile;
32 |
33 | /**
34 | * Output folder which contains ico and png files
35 | *
36 | */
37 | private $_outputFolder;
38 |
39 | /**
40 | * Imagine object
41 | *
42 | * @var \Imagine\Image\AbstractImagine
43 | */
44 | private $_imagine;
45 |
46 | /**
47 | * Include the 64x64 image in the ICO or not
48 | *
49 | * @var bool
50 | */
51 | private $_use64Icon;
52 |
53 | /**
54 | * Include the 48x48 image in the ICO or not
55 | *
56 | * @var bool
57 | */
58 | private $_use48Icon;
59 |
60 | /**
61 | * Exclude old apple touch images
62 | *
63 | * @var type
64 | */
65 | private $_noOldApple;
66 |
67 | /**
68 | * Exclude manifest.json and Android images
69 | *
70 | * @var type
71 | */
72 | private $_noAndroid;
73 |
74 | /**
75 | * Exclude Windows and IE tile images
76 | *
77 | * @var type
78 | */
79 | private $_noMs;
80 |
81 | /**
82 | * Android manifest app name
83 | *
84 | * @var string
85 | */
86 | private $_appName;
87 |
88 | protected function configure()
89 | {
90 | $this
91 | ->setName('generate')
92 | ->setDescription('Generate favicons from an original PNG image')
93 | ->addArgument('input', InputArgument::REQUIRED, 'Input PNG image')
94 | ->addArgument('output', InputArgument::OPTIONAL, 'Output folder', './')
95 | ->addOption('ico-64', null, InputOption::VALUE_NONE, 'Include 64x64 image in the ICO file (larger file size)')
96 | ->addOption('ico-48', null, InputOption::VALUE_NONE, 'Include 48x48 image in the ICO file (larger file size)')
97 | ->addOption('no-old-apple', null, InputOption::VALUE_NONE, 'Exclude old apple touch images')
98 | ->addOption('no-android', null, InputOption::VALUE_NONE, 'Exclude manifest.json and Android images')
99 | ->addOption('no-ms', null, InputOption::VALUE_NONE, 'Exclude Windows and IE tile images')
100 | ->addOption('app-name', null, InputOption::VALUE_REQUIRED, 'Android manifest app name', "")
101 | ;
102 | }
103 |
104 | protected function execute(InputInterface $input, OutputInterface $output)
105 | {
106 | $this->_prepare($input, $output);
107 |
108 | $this->_generateIco($output);
109 |
110 | $this->_generatePngs($output);
111 |
112 | if (!$this->_noAndroid)
113 | {
114 | $this->_generateManifestJson($output);
115 | }
116 | }
117 |
118 | private function _prepare(InputInterface $input, OutputInterface $output)
119 | {
120 | $this->_imagine = $this->_getImagine($output);
121 | $this->_outputFolder = rtrim($input->getArgument('output'), " /") . "/";
122 | $this->_inputFile = $input->getArgument('input');
123 | $this->_use64Icon = $input->getOption('ico-64');
124 | $this->_use48Icon = $input->getOption('ico-48');
125 | $this->_noOldApple = $input->getOption('no-old-apple');
126 | $this->_noAndroid = $input->getOption('no-android');
127 | $this->_noMs = $input->getOption('no-ms');
128 | $this->_appName = $input->getOption('app-name');
129 |
130 | if (!file_exists($this->_inputFile) && !is_file($this->_inputFile))
131 | {
132 | $output->writeln("Input file does not exist: {$this->_inputFile}");
133 | }
134 |
135 | if (!file_exists($this->_outputFolder) || !is_dir($this->_outputFolder))
136 | {
137 | mkdir($this->_outputFolder, 0755, true);
138 | }
139 | }
140 |
141 | /**
142 | * Generate ICO icon
143 | *
144 | * @param OutputInterface $output
145 | *
146 | * @return bool
147 | */
148 | private function _generateIco(OutputInterface $output)
149 | {
150 | $filename = $this->_outputFolder . "favicon.ico";
151 | $output->writeln("Creating: {$filename}");
152 |
153 |
154 | $originalImage = $this->_imagine->open($this->_inputFile)->strip();
155 | $originalImage->copy()
156 | ->resize(new \Imagine\Image\Box(16, 16))
157 | ->save('tmp-16.bmp');
158 |
159 | $icon = $this->_imagine->open('tmp-16.bmp');
160 | $icon->layers()
161 | ->add($originalImage->copy()->resize(new \Imagine\Image\Box(32, 32)));
162 |
163 | if ($this->_use48Icon)
164 | {
165 | $icon->layers()->add($originalImage->copy()->resize(new \Imagine\Image\Box(48, 48)));
166 | }
167 | if ($this->_use64Icon)
168 | {
169 | $icon->layers()->add($originalImage->copy()->resize(new \Imagine\Image\Box(64, 64)));
170 | }
171 |
172 | $icon->save($filename, array(
173 | 'flatten' => false
174 | ));
175 |
176 | unlink('tmp-16.bmp');
177 | return true;
178 | }
179 |
180 | private function _generatePngs(OutputInterface $output)
181 | {
182 | $sizes = Config::getSizes($this->_noOldApple, $this->_noAndroid, $this->_noMs);
183 | $originalImage = $this->_imagine->open($this->_inputFile)->strip();
184 | $pallete = new \Imagine\Image\Palette\RGB();
185 | $background = $pallete->color('#000', 0);
186 | foreach ($sizes as $imageName => $size)
187 | {
188 | $output->writeln("Creating: {$imageName}");
189 | $imagePath = $this->_outputFolder . $imageName;
190 | if (substr($imageName, 0, 6) == 'mstile' && $size != 144)
191 | {
192 | $opt = Config::getTileSettings($imageName);
193 | $tile = $this->_imagine->create(new \Imagine\Image\Box($opt['w'], $opt['h']), $background);
194 | $tile->paste(
195 | $originalImage->copy()->resize(new \Imagine\Image\Box($opt['icon'], $opt['icon'])), new \Imagine\Image\Point($opt['top'], $opt['left'])
196 | );
197 | $tile->save($imagePath);
198 | }
199 | else
200 | {
201 | $originalImage->copy()
202 | ->resize(new \Imagine\Image\Box($size, $size))
203 | ->save($imagePath);
204 | }
205 | }
206 | }
207 |
208 | private function _generateManifestJson(OutputInterface $output)
209 | {
210 | $output->writeln("Creating: manifest.json");
211 | $manifest = array(
212 | 'name' => $this->_appName,
213 | 'icons' => array(),
214 | );
215 | foreach (array(36, 48, 72, 96, 144, 192) as $size)
216 | {
217 | $manifest['icons'][] = array(
218 | 'src' => "/android-chrome-{$size}x{$size}.png",
219 | 'sizes' => "{$size}x{$size}",
220 | 'type' => "image/png",
221 | 'density' => round($size / 48.0, 2)
222 | );
223 | }
224 | $json = json_encode($manifest, JSON_PRETTY_PRINT);
225 | $jsonFilePath = $this->_outputFolder . "manifest.json";
226 | file_put_contents($jsonFilePath, $json);
227 | }
228 |
229 | /**
230 | * Get the imagine object based on the options. Default will use Imagick
231 | * extension
232 | *
233 | * @param InputInterface $input
234 | * @param OutputInterface $output
235 | * @return \Imagine\Image\AbstractImagine
236 | */
237 | private function _getImagine(OutputInterface $output)
238 | {
239 | // Use Imagick extension by default
240 | if (extension_loaded('imagick') && class_exists("Imagick"))
241 | {
242 | $output->writeln('Imagick library is used!');
243 | return new \Imagine\Imagick\Imagine();
244 | }
245 | // fall back to GD
246 | if (extension_loaded('gd') && function_exists('gd_info'))
247 | {
248 | $output->writeln('GD library is used!');
249 | return new \Imagine\Gd\Imagine();
250 | }
251 | // Quit if can not find both extensions
252 | $output->writeln("The `imagick` or `gd` extension must be loaded to generate image");
253 | exit(1);
254 | }
255 |
256 | }
257 |
--------------------------------------------------------------------------------
/tests/HieuLe/Favicon/HtmlTest.php:
--------------------------------------------------------------------------------
1 |
9 | */
10 | class HtmlTest extends \PHPUnit_Framework_TestCase
11 | {
12 |
13 | public function testOutputNoConfig()
14 | {
15 | $expected = array(
16 | '',
17 | '',
18 | '',
19 | '',
20 | '',
21 | '',
22 | '',
23 | '',
24 | '',
25 | '',
26 | '',
27 | '',
28 | '',
29 | '',
30 | '',
31 | '',
32 | '',
33 | '',
34 | '',
35 | );
36 | $html = Html::output();
37 | $this->assertEquals(implode("\n", $expected), $html);
38 | }
39 |
40 | public function testOutputNoOldApple()
41 | {
42 | $expected = array(
43 | '',
44 | '',
45 | '',
46 | '',
47 | '',
48 | '',
49 | '',
50 | '',
51 | '',
52 | '',
53 | '',
54 | '',
55 | '',
56 | '',
57 | '',
58 | );
59 | $html = Html::output(true);
60 | $this->assertEquals(implode("\n", $expected), $html);
61 | }
62 |
63 | public function testOutputNoAndroid()
64 | {
65 | $expected = array(
66 | '',
67 | '',
68 | '',
69 | '',
70 | '',
71 | '',
72 | '',
73 | '',
74 | '',
75 | '',
76 | '',
77 | '',
78 | '',
79 | '',
80 | '',
81 | '',
82 | '',
83 | '',
84 | );
85 | $html = Html::output(false, true);
86 | $this->assertEquals(implode("\n", $expected), $html);
87 | }
88 |
89 | public function testOutputNoMs()
90 | {
91 | $expected = array(
92 | '',
93 | '',
94 | '',
95 | '',
96 | '',
97 | '',
98 | '',
99 | '',
100 | '',
101 | '',
102 | '',
103 | '',
104 | );
105 | $html = Html::output(false, false, true);
106 | $this->assertEquals(implode("\n", $expected), $html);
107 | }
108 |
109 | public function testOutputFullMsInfo()
110 | {
111 | $expected = array(
112 | '',
113 | '',
114 | '',
115 | '',
116 | '',
117 | '',
118 | '',
119 | '',
120 | '',
121 | '',
122 | '',
123 | '',
124 | '',
125 | '',
126 | '',
127 | '',
128 | '',
129 | '',
130 | '',
131 | '',
132 | );
133 | $html = Html::output(false, false, false, '#F0F0F0', 'IEConfig.xml', 'Foo App');
134 | $this->assertEquals(implode("\n", $expected), $html);
135 | }
136 |
137 | public function testHelperFunctionWithMs()
138 | {
139 | $expected = array(
140 | '',
141 | '',
142 | '',
143 | '',
144 | '',
145 | '',
146 | '',
147 | '',
148 | '',
149 | '',
150 | '',
151 | '',
152 | '',
153 | '',
154 | '',
155 | '',
156 | '',
157 | '',
158 | '',
159 | '',
160 | );
161 | $html = favicon(FAVICON_ENABLE_ALL, array(
162 | 'tile_color' => '#F0F0F0',
163 | 'browser_config_file' => 'IEConfig.xml',
164 | 'application_name' => 'Foo App'
165 | ));
166 | $this->assertEquals(implode("\n", $expected), $html);
167 | }
168 |
169 | public function testHelperFunctionMinimal()
170 | {
171 | $expected = array(
172 | '',
173 | '',
174 | '',
175 | '',
176 | '',
177 | '',
178 | '',
179 | );
180 | $html = favicon(FAVICON_NO_OLD_APPLE | FAVICON_NO_ANDROID | FAVICON_NO_MS);
181 | $this->assertEquals(implode("\n", $expected), $html);
182 | }
183 |
184 | }
185 |
--------------------------------------------------------------------------------