73 Commits

Author SHA1 Message Date
El RIDO
b2ef205411 extended script to test jdenticon ImageMagick and documented option to work without GD 2022-11-07 19:42:20 +01:00
El RIDO
c0758e7bbb correct labels, Jdenticon renders PNG or SVG, both in pure PHP 2022-11-07 19:09:16 +01:00
El RIDO
89d575ace3 in light of the perf/size test results of Jdenticons, switch back to Identicons as the default 2022-10-30 09:24:35 +01:00
El RIDO
1892264cf0 add perf/size test for Jdenticons 2022-10-30 09:04:27 +01:00
El RIDO
f5332ee6ff Merge pull request #1005 from PrivateBin/crowdin-translation
New Crowdin updates
2022-10-29 15:07:46 +02:00
PrivateBin Translator Bot
e39a2eeb61 New translations en.json (Corsican) 2022-10-29 13:09:30 +02:00
El RIDO
405067668a Merge pull request #1004 from PrivateBin/dependabot/composer/phpunit/phpunit-5.6.3
Bump phpunit/phpunit from 5.2.7 to 5.6.3
2022-10-29 08:17:33 +02:00
dependabot[bot]
922feb2779 Bump phpunit/phpunit from 5.2.7 to 5.6.3
Bumps [phpunit/phpunit](https://github.com/sebastianbergmann/phpunit) from 5.2.7 to 5.6.3.
- [Release notes](https://github.com/sebastianbergmann/phpunit/releases)
- [Changelog](https://github.com/sebastianbergmann/phpunit/blob/main/ChangeLog-8.5.md)
- [Commits](https://github.com/sebastianbergmann/phpunit/compare/5.2.7...5.6.3)

---
updated-dependencies:
- dependency-name: phpunit/phpunit
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-10-29 06:07:06 +00:00
El RIDO
968d7a19bf Merge pull request #1001 from PrivateBin/jdenticon
add new Jdenticon comment icon library and set it as default
2022-10-29 08:06:43 +02:00
El RIDO
795c030bdb Revert several commits that didn't solve the Scrutinizer issues 2022-10-26 08:16:30 +02:00
El RIDO
613e4e9d57 custom php-cs doesn't support website config on Scrutinizer 2022-10-26 08:11:35 +02:00
El RIDO
c91d20ae75 try using the updated php-cs dependency on Scrutinizer 2022-10-26 08:03:55 +02:00
El RIDO
d5104a1d63 Merge pull request #1000 from PrivateBin/set-output-deprecation
handle github actions deprecation warnings
2022-10-26 07:57:33 +02:00
El RIDO
78b3630eb5 try updating that borked php-cs dependency on Scrutinizer 2022-10-26 07:55:26 +02:00
El RIDO
31f75ee138 undo accidental PHP 8 induced upgrade of php-timer, making it incompatible with PHP 7 used on Scrutinizer 2022-10-26 07:39:35 +02:00
El RIDO
ae3a0b19ee update PHP version used in Scrutinizer CI to 7.4, fixing php-timer requirement 2022-10-26 07:23:37 +02:00
El RIDO
d5e7e6e2ab document Jdenticon change 2022-10-26 07:11:02 +02:00
El RIDO
8ac69590cf add new Jdenticon comment icon library, set it as default, fixes #793 2022-10-26 06:53:56 +02:00
El RIDO
ba4878056b misleading documentation 2022-10-26 05:51:36 +02:00
El RIDO
ae6248e27e handle github actions deprecation warnings
see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
2022-10-26 05:48:51 +02:00
El RIDO
78aa70e3ab Merge pull request #999 from PrivateBin/php82-deprecated-callables
PHP 8.2 compatibility: Use of "self" in callables is deprecated
2022-10-26 04:39:11 +02:00
El RIDO
670f9ef548 Merge pull request #998 from PrivateBin/crowdin-translation
New Crowdin updates
2022-10-25 21:11:30 +02:00
PrivateBin Translator Bot
1a828884d1 New translations en.json (Hungarian) 2022-10-25 21:01:18 +02:00
PrivateBin Translator Bot
18a957ee54 New translations en.json (Hebrew) 2022-10-25 21:01:17 +02:00
El RIDO
8f8adb9b0d Merge branch 'master' into crowdin-translation 2022-10-25 20:27:22 +02:00
PrivateBin Translator Bot
c3041924b6 New translations en.json (Occitan) 2022-10-25 20:03:32 +02:00
PrivateBin Translator Bot
5584fdb347 New translations en.json (Chinese Simplified) 2022-10-25 20:03:31 +02:00
PrivateBin Translator Bot
ec75ef6e36 New translations en.json (Ukrainian) 2022-10-25 20:03:30 +02:00
PrivateBin Translator Bot
b1f24a51c8 New translations en.json (Turkish) 2022-10-25 20:03:29 +02:00
PrivateBin Translator Bot
60b091d0e1 New translations en.json (Slovak) 2022-10-25 20:03:28 +02:00
PrivateBin Translator Bot
ed5859c6b6 New translations en.json (Russian) 2022-10-25 20:03:27 +02:00
PrivateBin Translator Bot
cd0a7effa0 New translations en.json (Portuguese) 2022-10-25 20:03:25 +02:00
PrivateBin Translator Bot
ab3e8ffd49 New translations en.json (Norwegian) 2022-10-25 20:03:24 +02:00
PrivateBin Translator Bot
7269755bc2 New translations en.json (Dutch) 2022-10-25 20:03:23 +02:00
PrivateBin Translator Bot
5e48a927f2 New translations en.json (Lithuanian) 2022-10-25 20:03:22 +02:00
El RIDO
bff4d3a016 PHP 8.2 compatibility: Use of "self" in callables is deprecated 2022-10-25 07:15:09 +02:00
PrivateBin Translator Bot
2eab8adcd1 New translations en.json (Corsican) 2022-10-25 07:05:13 +02:00
PrivateBin Translator Bot
92c42afdc7 New translations en.json (Arabic) 2022-10-25 07:05:12 +02:00
PrivateBin Translator Bot
da48f8a9a1 New translations en.json (Bulgarian) 2022-10-25 07:05:11 +02:00
PrivateBin Translator Bot
7b40893497 New translations en.json (Catalan) 2022-10-25 07:05:10 +02:00
PrivateBin Translator Bot
0a1fccea11 New translations en.json (Czech) 2022-10-25 07:05:09 +02:00
PrivateBin Translator Bot
3de21004c4 New translations en.json (German) 2022-10-25 07:05:07 +02:00
PrivateBin Translator Bot
d0d0621bac New translations en.json (Greek) 2022-10-25 07:05:05 +02:00
PrivateBin Translator Bot
3845298804 New translations en.json (Finnish) 2022-10-25 07:05:04 +02:00
PrivateBin Translator Bot
d6c5f97d58 New translations en.json (Hebrew) 2022-10-25 07:05:03 +02:00
PrivateBin Translator Bot
f713b92893 New translations en.json (Hungarian) 2022-10-25 07:05:01 +02:00
PrivateBin Translator Bot
f254285b01 New translations en.json (Italian) 2022-10-25 07:05:00 +02:00
PrivateBin Translator Bot
c2d5c6fc43 New translations en.json (Japanese) 2022-10-25 07:04:59 +02:00
PrivateBin Translator Bot
3d4f9cc1b3 New translations en.json (Kurdish) 2022-10-25 07:04:58 +02:00
PrivateBin Translator Bot
945435e133 New translations en.json (Lithuanian) 2022-10-25 07:04:57 +02:00
PrivateBin Translator Bot
d288f2ab73 New translations en.json (Dutch) 2022-10-25 07:04:56 +02:00
PrivateBin Translator Bot
7ab3f3f271 New translations en.json (Spanish) 2022-10-25 07:04:54 +02:00
PrivateBin Translator Bot
019c8b64b8 New translations en.json (Norwegian) 2022-10-25 07:04:53 +02:00
PrivateBin Translator Bot
06b01c57ac New translations en.json (Portuguese) 2022-10-25 07:04:52 +02:00
PrivateBin Translator Bot
b756da5839 New translations en.json (Russian) 2022-10-25 07:04:51 +02:00
PrivateBin Translator Bot
8a9761b430 New translations en.json (Slovak) 2022-10-25 07:04:50 +02:00
PrivateBin Translator Bot
79c4ace626 New translations en.json (Slovenian) 2022-10-25 07:04:49 +02:00
PrivateBin Translator Bot
534d014254 New translations en.json (Swedish) 2022-10-25 07:04:48 +02:00
PrivateBin Translator Bot
40a037b625 New translations en.json (Turkish) 2022-10-25 07:04:47 +02:00
PrivateBin Translator Bot
5cb40e1f9e New translations en.json (Ukrainian) 2022-10-25 07:04:46 +02:00
PrivateBin Translator Bot
c9144def9f New translations en.json (Chinese Simplified) 2022-10-25 07:04:45 +02:00
PrivateBin Translator Bot
989fe6cc3a New translations en.json (Indonesian) 2022-10-25 07:04:44 +02:00
PrivateBin Translator Bot
08ac373b12 New translations en.json (Estonian) 2022-10-25 07:04:42 +02:00
PrivateBin Translator Bot
c2ec38e0a3 New translations en.json (Hindi) 2022-10-25 07:04:41 +02:00
PrivateBin Translator Bot
f31e384980 New translations en.json (Lojban) 2022-10-25 07:04:39 +02:00
PrivateBin Translator Bot
a1b4fcdefb New translations en.json (Latin) 2022-10-25 07:04:39 +02:00
PrivateBin Translator Bot
09902b9d67 New translations en.json (Occitan) 2022-10-25 07:04:38 +02:00
PrivateBin Translator Bot
f8bb6efcf9 New translations en.json (Polish) 2022-10-25 07:04:37 +02:00
PrivateBin Translator Bot
930063442a New translations en.json (French) 2022-10-25 07:04:35 +02:00
El RIDO
849c1c7cd1 fix display of configured name in twitter title 2022-10-25 06:34:40 +02:00
PrivateBin Translator Bot
5e513588db New translations en.json (German) 2022-10-25 06:08:09 +02:00
PrivateBin Translator Bot
fef83a37ef New translations en.json (Slovak) 2022-10-23 22:46:25 +02:00
El RIDO
4f9af21fb2 Merge pull request #997 from jmozd/serverside_yourls
Serverside yourls
2022-10-23 18:03:14 +02:00
91 changed files with 7081 additions and 253 deletions

View File

@@ -65,14 +65,13 @@ jobs:
- name: Get composer cache directory
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
# http://man7.org/linux/man-pages/man1/date.1.html
# https://github.com/actions/cache#creating-a-cache-key
- name: Get Date
id: get-date
run: |
echo "::set-output name=date::$(/bin/date -u "+%Y%m%d")"
run: echo "date=$(/bin/date -u "+%Y%m%d")" >> $GITHUB_OUTPUT
shell: bash
- name: Cache dependencies

View File

@@ -3,6 +3,7 @@
* **1.4.1 (not yet released)**
* ADDED: Translations for Turkish, Slovak and Greek
* ADDED: S3 Storage backend (#994)
* ADDED: Jdenticons as an option for comment icons (#793)
* CHANGED: Avoid `SUPER` privilege for setting the `sql_mode` for MariaDB/MySQL (#919)
* CHANGED: Upgrading libraries to: zlib 1.2.13
* FIXED: Revert to CREATE INDEX without IF NOT EXISTS clauses, to support MySQL (#943)

View File

@@ -26,7 +26,7 @@ install and configure PrivateBin on your server. It's available on
- `open_basedir` access to `/dev/urandom`
- mcrypt extension AND `open_basedir` access to `/dev/urandom`
- com_dotnet extension
- GD extension
- GD extension (when using identicon or vizhash icons, jdenticon works without it)
- zlib extension
- some disk space or a database supported by [PDO](https://php.net/manual/book.pdo.php)
- ability to create files and folders in the installation directory and the PATH

View File

@@ -65,10 +65,11 @@ languageselection = false
; qrcode = true
; (optional) IP based icons are a weak mechanism to detect if a comment was from
; a different user when the same username was used in a comment. It might be
; used to get the IP of a non anonymous comment poster if the server salt is
; leaked and a SHA256 HMAC rainbow table is generated for all (relevant) IPs.
; Can be set to one these values: "none" / "vizhash" / "identicon" (default).
; a different user when the same username was used in a comment. It might get
; used to get the IP of a comment poster if the server salt is leaked and a
; SHA512 HMAC rainbow table is generated for all (relevant) IPs.
; Can be set to one these values:
; "none" / "identicon" (default) / "jdenticon" / "vizhash".
; icon = "none"
; Content Security Policy headers allow a website to restrict what sources are

View File

@@ -27,7 +27,8 @@
"php" : "^5.6.0 || ^7.0 || ^8.0",
"paragonie/random_compat" : "2.0.21",
"yzalis/identicon" : "2.0.0",
"mlocati/ip-lib" : "1.18.0"
"mlocati/ip-lib" : "1.18.0",
"jdenticon/jdenticon": "^1.0"
},
"suggest" : {
"google/cloud-storage" : "1.26.1",

467
composer.lock generated
View File

@@ -4,8 +4,57 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "fa52d4988bfe17d4b27e3a4789a1ec49",
"content-hash": "17bceced29627163f7aa330a0697f68b",
"packages": [
{
"name": "jdenticon/jdenticon",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/dmester/jdenticon-php.git",
"reference": "994ee07293fb978f983393ffcb2c0250592a6ac4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/dmester/jdenticon-php/zipball/994ee07293fb978f983393ffcb2c0250592a6ac4",
"reference": "994ee07293fb978f983393ffcb2c0250592a6ac4",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
},
"require-dev": {
"phpunit/phpunit": "^5.7"
},
"type": "library",
"autoload": {
"psr-4": {
"Jdenticon\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Daniel Mester Pirttijärvi"
}
],
"description": "Render PNG and SVG identicons.",
"homepage": "https://jdenticon.com/",
"keywords": [
"avatar",
"identicon",
"jdenticon"
],
"support": {
"docs": "https://jdenticon.com/php-api.html",
"issues": "https://github.com/dmester/jdenticon-php/issues",
"source": "https://github.com/dmester/jdenticon-php"
},
"time": "2022-07-02T11:03:15+00:00"
},
{
"name": "mlocati/ip-lib",
"version": "1.18.0",
@@ -61,6 +110,10 @@
"range",
"subnet"
],
"support": {
"issues": "https://github.com/mlocati/ip-lib/issues",
"source": "https://github.com/mlocati/ip-lib/tree/1.18.0"
},
"funding": [
{
"url": "https://github.com/sponsors/mlocati",
@@ -120,6 +173,11 @@
"pseudorandom",
"random"
],
"support": {
"email": "info@paragonie.com",
"issues": "https://github.com/paragonie/random_compat/issues",
"source": "https://github.com/paragonie/random_compat"
},
"time": "2022-02-16T17:07:03+00:00"
},
{
@@ -172,6 +230,10 @@
"identicon",
"image"
],
"support": {
"issues": "https://github.com/yzalis/Identicon/issues",
"source": "https://github.com/yzalis/Identicon/tree/master"
},
"abandoned": true,
"time": "2019-10-14T09:30:57+00:00"
}
@@ -179,31 +241,34 @@
"packages-dev": [
{
"name": "doctrine/instantiator",
"version": "1.4.0",
"version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/doctrine/instantiator.git",
"reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b"
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/d56bf6102915de5702778fe20f2de3b2fe570b5b",
"reference": "d56bf6102915de5702778fe20f2de3b2fe570b5b",
"url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d",
"reference": "8e884e78f9f0eb1329e445619e04456e64d8051d",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
"php": ">=5.3,<8.0-DEV"
},
"require-dev": {
"doctrine/coding-standard": "^8.0",
"athletic/athletic": "~0.1.8",
"ext-pdo": "*",
"ext-phar": "*",
"phpbench/phpbench": "^0.13 || 1.0.0-alpha2",
"phpstan/phpstan": "^0.12",
"phpstan/phpstan-phpunit": "^0.12",
"phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
"phpunit/phpunit": "~4.0",
"squizlabs/php_codesniffer": "~2.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/"
@@ -217,55 +282,42 @@
{
"name": "Marco Pivetta",
"email": "ocramius@gmail.com",
"homepage": "https://ocramius.github.io/"
"homepage": "http://ocramius.github.com/"
}
],
"description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors",
"homepage": "https://www.doctrine-project.org/projects/instantiator.html",
"homepage": "https://github.com/doctrine/instantiator",
"keywords": [
"constructor",
"instantiate"
],
"funding": [
{
"url": "https://www.doctrine-project.org/sponsorship.html",
"type": "custom"
"support": {
"issues": "https://github.com/doctrine/instantiator/issues",
"source": "https://github.com/doctrine/instantiator/tree/master"
},
{
"url": "https://www.patreon.com/phpdoctrine",
"type": "patreon"
},
{
"url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
"type": "tidelift"
}
],
"time": "2020-11-10T18:47:58+00:00"
"time": "2015-06-14T21:17:01+00:00"
},
{
"name": "myclabs/deep-copy",
"version": "1.10.2",
"version": "1.7.0",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220"
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/776f831124e9c62e1a2c601ecc52e776d8bb7220",
"reference": "776f831124e9c62e1a2c601ecc52e776d8bb7220",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
"reference": "3b8a3a99ba1f6a3952ac2747d989303cbd6b7a3e",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"replace": {
"myclabs/deep-copy": "self.version"
"php": "^5.6 || ^7.0"
},
"require-dev": {
"doctrine/collections": "^1.0",
"doctrine/common": "^2.6",
"phpunit/phpunit": "^7.1"
"phpunit/phpunit": "^4.1"
},
"type": "library",
"autoload": {
@@ -288,40 +340,43 @@
"object",
"object graph"
],
"funding": [
{
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
"type": "tidelift"
}
],
"time": "2020-11-13T09:40:50+00:00"
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.x"
},
"time": "2017-10-19T19:58:43+00:00"
},
{
"name": "phpdocumentor/reflection-common",
"version": "2.2.0",
"version": "1.0.1",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionCommon.git",
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
"reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"reference": "21bdeb5f65d7ebf9f43b1b25d404f87deab5bfb6",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
"php": ">=5.5"
},
"require-dev": {
"phpunit/phpunit": "^4.6"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-2.x": "2.x-dev"
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src/"
"phpDocumentor\\Reflection\\": [
"src"
]
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -343,42 +398,42 @@
"reflection",
"static analysis"
],
"time": "2020-06-27T09:03:43+00:00"
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionCommon/issues",
"source": "https://github.com/phpDocumentor/ReflectionCommon/tree/master"
},
"time": "2017-09-11T18:02:19+00:00"
},
{
"name": "phpdocumentor/reflection-docblock",
"version": "5.3.0",
"version": "3.3.2",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170"
"reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170",
"reference": "622548b623e81ca6d78b721c5e029f4ce664f170",
"url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/bf329f6c1aadea3299f08ee804682b7c45b326a2",
"reference": "bf329f6c1aadea3299f08ee804682b7c45b326a2",
"shasum": ""
},
"require": {
"ext-filter": "*",
"php": "^7.2 || ^8.0",
"phpdocumentor/reflection-common": "^2.2",
"phpdocumentor/type-resolver": "^1.3",
"webmozart/assert": "^1.9.1"
"php": "^5.6 || ^7.0",
"phpdocumentor/reflection-common": "^1.0.0",
"phpdocumentor/type-resolver": "^0.4.0",
"webmozart/assert": "^1.0"
},
"require-dev": {
"mockery/mockery": "~1.3.2",
"psalm/phar": "^4.8"
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^4.4"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src"
"phpDocumentor\\Reflection\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -389,46 +444,48 @@
{
"name": "Mike van Riel",
"email": "me@mikevanriel.com"
},
{
"name": "Jaap van Otterdijk",
"email": "account@ijaap.nl"
}
],
"description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
"time": "2021-10-19T17:43:47+00:00"
"support": {
"issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues",
"source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/release/3.x"
},
"time": "2017-11-10T14:09:06+00:00"
},
{
"name": "phpdocumentor/type-resolver",
"version": "1.6.0",
"version": "0.4.0",
"source": {
"type": "git",
"url": "https://github.com/phpDocumentor/TypeResolver.git",
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706"
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/93ebd0014cab80c4ea9f5e297ea48672f1b87706",
"reference": "93ebd0014cab80c4ea9f5e297ea48672f1b87706",
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7",
"reference": "9c977708995954784726e25d0cd1dddf4e65b0f7",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"phpdocumentor/reflection-common": "^2.0"
"php": "^5.5 || ^7.0",
"phpdocumentor/reflection-common": "^1.0"
},
"require-dev": {
"ext-tokenizer": "*",
"psalm/phar": "^4.8"
"mockery/mockery": "^0.9.4",
"phpunit/phpunit": "^5.2||^4.8.24"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-1.x": "1.x-dev"
"dev-master": "1.0.x-dev"
}
},
"autoload": {
"psr-4": {
"phpDocumentor\\Reflection\\": "src"
"phpDocumentor\\Reflection\\": [
"src/"
]
}
},
"notification-url": "https://packagist.org/downloads/",
@@ -441,8 +498,11 @@
"email": "me@mikevanriel.com"
}
],
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
"time": "2022-01-04T19:58:01+00:00"
"support": {
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
"source": "https://github.com/phpDocumentor/TypeResolver/tree/master"
},
"time": "2017-07-14T14:27:02+00:00"
},
{
"name": "phpspec/prophecy",
@@ -505,6 +565,10 @@
"spy",
"stub"
],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/v1.10.3"
},
"time": "2020-03-05T15:02:03+00:00"
},
{
@@ -568,6 +632,11 @@
"testing",
"xunit"
],
"support": {
"irc": "irc://irc.freenode.net/phpunit",
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/4.0"
},
"time": "2017-04-02T07:44:40+00:00"
},
{
@@ -615,6 +684,11 @@
"filesystem",
"iterator"
],
"support": {
"irc": "irc://irc.freenode.net/phpunit",
"issues": "https://github.com/sebastianbergmann/php-file-iterator/issues",
"source": "https://github.com/sebastianbergmann/php-file-iterator/tree/1.4.5"
},
"time": "2017-11-27T13:52:08+00:00"
},
{
@@ -656,6 +730,10 @@
"keywords": [
"template"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-text-template/issues",
"source": "https://github.com/sebastianbergmann/php-text-template/tree/1.2.1"
},
"time": "2015-06-21T13:50:34+00:00"
},
{
@@ -705,33 +783,37 @@
"keywords": [
"timer"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-timer/issues",
"source": "https://github.com/sebastianbergmann/php-timer/tree/master"
},
"time": "2017-02-26T11:10:40+00:00"
},
{
"name": "phpunit/php-token-stream",
"version": "2.0.2",
"version": "1.4.12",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-token-stream.git",
"reference": "791198a2c6254db10131eecfe8c06670700904db"
"reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/791198a2c6254db10131eecfe8c06670700904db",
"reference": "791198a2c6254db10131eecfe8c06670700904db",
"url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/1ce90ba27c42e4e44e6d8458241466380b51fa16",
"reference": "1ce90ba27c42e4e44e6d8458241466380b51fa16",
"shasum": ""
},
"require": {
"ext-tokenizer": "*",
"php": "^7.0"
"php": ">=5.3.3"
},
"require-dev": {
"phpunit/phpunit": "^6.2.4"
"phpunit/phpunit": "~4.2"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0-dev"
"dev-master": "1.4-dev"
}
},
"autoload": {
@@ -754,21 +836,25 @@
"keywords": [
"tokenizer"
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-token-stream/issues",
"source": "https://github.com/sebastianbergmann/php-token-stream/tree/1.4"
},
"abandoned": true,
"time": "2017-11-27T05:48:46+00:00"
"time": "2017-12-04T08:55:13+00:00"
},
{
"name": "phpunit/phpunit",
"version": "5.7.27",
"version": "5.6.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c"
"reference": "a9de0dbafeb6b1391b391fbb034734cb0af9f67c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c",
"reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a9de0dbafeb6b1391b391fbb034734cb0af9f67c",
"reference": "a9de0dbafeb6b1391b391fbb034734cb0af9f67c",
"shasum": ""
},
"require": {
@@ -779,21 +865,21 @@
"ext-xml": "*",
"myclabs/deep-copy": "~1.3",
"php": "^5.6 || ^7.0",
"phpspec/prophecy": "^1.6.2",
"phpunit/php-code-coverage": "^4.0.4",
"phpspec/prophecy": "^1.3.1",
"phpunit/php-code-coverage": "^4.0.1",
"phpunit/php-file-iterator": "~1.4",
"phpunit/php-text-template": "~1.2",
"phpunit/php-timer": "^1.0.6",
"phpunit/phpunit-mock-objects": "^3.2",
"sebastian/comparator": "^1.2.4",
"sebastian/diff": "^1.4.3",
"sebastian/environment": "^1.3.4 || ^2.0",
"sebastian/exporter": "~2.0",
"sebastian/global-state": "^1.1",
"sebastian/object-enumerator": "~2.0",
"sebastian/comparator": "~1.1",
"sebastian/diff": "~1.2",
"sebastian/environment": "^1.3 || ^2.0",
"sebastian/exporter": "~1.2",
"sebastian/global-state": "~1.0",
"sebastian/object-enumerator": "~1.0",
"sebastian/resource-operations": "~1.0",
"sebastian/version": "^1.0.6|^2.0.1",
"symfony/yaml": "~2.1|~3.0|~4.0"
"sebastian/version": "~1.0|~2.0",
"symfony/yaml": "~2.1|~3.0"
},
"conflict": {
"phpdocumentor/reflection-docblock": "3.0.2"
@@ -811,7 +897,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "5.7.x-dev"
"dev-master": "5.6.x-dev"
}
},
"autoload": {
@@ -837,7 +923,11 @@
"testing",
"xunit"
],
"time": "2018-02-01T05:50:59+00:00"
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"source": "https://github.com/sebastianbergmann/phpunit/tree/5.6.3"
},
"time": "2016-11-14T06:39:40+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@@ -896,6 +986,11 @@
"mock",
"xunit"
],
"support": {
"irc": "irc://irc.freenode.net/phpunit",
"issues": "https://github.com/sebastianbergmann/phpunit-mock-objects/issues",
"source": "https://github.com/sebastianbergmann/phpunit-mock-objects/tree/3.4"
},
"abandoned": true,
"time": "2017-06-30T09:13:00+00:00"
},
@@ -942,6 +1037,10 @@
],
"description": "Looks up which function or method a line of code belongs to",
"homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/",
"support": {
"issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues",
"source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/1.0.2"
},
"funding": [
{
"url": "https://github.com/sebastianbergmann",
@@ -1012,6 +1111,10 @@
"compare",
"equality"
],
"support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues",
"source": "https://github.com/sebastianbergmann/comparator/tree/1.2"
},
"time": "2017-01-29T09:50:25+00:00"
},
{
@@ -1064,6 +1167,10 @@
"keywords": [
"diff"
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
"source": "https://github.com/sebastianbergmann/diff/tree/1.4"
},
"time": "2017-05-22T07:24:03+00:00"
},
{
@@ -1114,25 +1221,29 @@
"environment",
"hhvm"
],
"support": {
"issues": "https://github.com/sebastianbergmann/environment/issues",
"source": "https://github.com/sebastianbergmann/environment/tree/master"
},
"time": "2016-11-26T07:53:53+00:00"
},
{
"name": "sebastian/exporter",
"version": "2.0.0",
"version": "1.2.2",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4"
"reference": "42c4c2eec485ee3e159ec9884f95b431287edde4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
"reference": "ce474bdd1a34744d7ac5d6aad3a46d48d9bac4c4",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4",
"reference": "42c4c2eec485ee3e159ec9884f95b431287edde4",
"shasum": ""
},
"require": {
"php": ">=5.3.3",
"sebastian/recursion-context": "~2.0"
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"ext-mbstring": "*",
@@ -1141,7 +1252,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "1.3.x-dev"
}
},
"autoload": {
@@ -1181,7 +1292,11 @@
"export",
"exporter"
],
"time": "2016-11-19T08:54:04+00:00"
"support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues",
"source": "https://github.com/sebastianbergmann/exporter/tree/master"
},
"time": "2016-06-17T09:04:28+00:00"
},
{
"name": "sebastian/global-state",
@@ -1232,25 +1347,29 @@
"keywords": [
"global state"
],
"support": {
"issues": "https://github.com/sebastianbergmann/global-state/issues",
"source": "https://github.com/sebastianbergmann/global-state/tree/1.1.1"
},
"time": "2015-10-12T03:26:01+00:00"
},
{
"name": "sebastian/object-enumerator",
"version": "2.0.1",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/object-enumerator.git",
"reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7"
"reference": "d4ca2fb70344987502567bc50081c03e6192fb26"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1311872ac850040a79c3c058bea3e22d0f09cbb7",
"reference": "1311872ac850040a79c3c058bea3e22d0f09cbb7",
"url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/d4ca2fb70344987502567bc50081c03e6192fb26",
"reference": "d4ca2fb70344987502567bc50081c03e6192fb26",
"shasum": ""
},
"require": {
"php": ">=5.6",
"sebastian/recursion-context": "~2.0"
"sebastian/recursion-context": "~1.0"
},
"require-dev": {
"phpunit/phpunit": "~5"
@@ -1258,7 +1377,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "1.0.x-dev"
}
},
"autoload": {
@@ -1278,20 +1397,24 @@
],
"description": "Traverses array structures and object graphs to enumerate all referenced objects",
"homepage": "https://github.com/sebastianbergmann/object-enumerator/",
"time": "2017-02-18T15:18:39+00:00"
"support": {
"issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
"source": "https://github.com/sebastianbergmann/object-enumerator/tree/master"
},
"time": "2016-01-28T13:25:10+00:00"
},
{
"name": "sebastian/recursion-context",
"version": "2.0.0",
"version": "1.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a"
"reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/2c3ba150cbec723aa057506e73a8d33bdb286c9a",
"reference": "2c3ba150cbec723aa057506e73a8d33bdb286c9a",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
"reference": "b19cc3298482a335a95f3016d2f8a6950f0fbcd7",
"shasum": ""
},
"require": {
@@ -1303,7 +1426,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "2.0.x-dev"
"dev-master": "1.0.x-dev"
}
},
"autoload": {
@@ -1331,7 +1454,11 @@
],
"description": "Provides functionality to recursively process PHP variables",
"homepage": "http://www.github.com/sebastianbergmann/recursion-context",
"time": "2016-11-19T07:33:16+00:00"
"support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/master"
},
"time": "2016-10-03T07:41:43+00:00"
},
{
"name": "sebastian/resource-operations",
@@ -1373,6 +1500,10 @@
],
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"support": {
"issues": "https://github.com/sebastianbergmann/resource-operations/issues",
"source": "https://github.com/sebastianbergmann/resource-operations/tree/master"
},
"time": "2015-07-28T20:34:47+00:00"
},
{
@@ -1416,27 +1547,28 @@
],
"description": "Library that helps with managing the version number of Git-hosted PHP projects",
"homepage": "https://github.com/sebastianbergmann/version",
"support": {
"issues": "https://github.com/sebastianbergmann/version/issues",
"source": "https://github.com/sebastianbergmann/version/tree/master"
},
"time": "2016-10-03T07:35:21+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.24.0",
"version": "v1.19.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
"reference": "aed596913b70fae57be53d86faa2e9ef85a2297b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/aed596913b70fae57be53d86faa2e9ef85a2297b",
"reference": "aed596913b70fae57be53d86faa2e9ef85a2297b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
"provide": {
"ext-ctype": "*"
"php": ">=5.3.3"
},
"suggest": {
"ext-ctype": "For best performance"
@@ -1444,7 +1576,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
"dev-main": "1.19-dev"
},
"thanks": {
"name": "symfony/polyfill",
@@ -1452,12 +1584,12 @@
}
},
"autoload": {
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
},
"files": [
"bootstrap.php"
]
],
"psr-4": {
"Symfony\\Polyfill\\Ctype\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
@@ -1481,6 +1613,9 @@
"polyfill",
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.19.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -1495,31 +1630,31 @@
"type": "tidelift"
}
],
"time": "2021-10-20T20:35:02+00:00"
"time": "2020-10-23T09:01:57+00:00"
},
{
"name": "symfony/yaml",
"version": "v4.4.37",
"version": "v3.4.47",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311"
"reference": "88289caa3c166321883f67fe5130188ebbb47094"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/d7f637cc0f0cc14beb0984f2bb50da560b271311",
"reference": "d7f637cc0f0cc14beb0984f2bb50da560b271311",
"url": "https://api.github.com/repos/symfony/yaml/zipball/88289caa3c166321883f67fe5130188ebbb47094",
"reference": "88289caa3c166321883f67fe5130188ebbb47094",
"shasum": ""
},
"require": {
"php": ">=7.1.3",
"php": "^5.5.9|>=7.0.8",
"symfony/polyfill-ctype": "~1.8"
},
"conflict": {
"symfony/console": "<3.4"
},
"require-dev": {
"symfony/console": "^3.4|^4.0|^5.0"
"symfony/console": "~3.4|~4.0"
},
"suggest": {
"symfony/console": "For validating YAML files using the lint command"
@@ -1547,8 +1682,11 @@
"homepage": "https://symfony.com/contributors"
}
],
"description": "Loads and dumps YAML files",
"description": "Symfony Yaml Component",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v3.4.47"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
@@ -1563,39 +1701,34 @@
"type": "tidelift"
}
],
"time": "2022-01-24T20:11:01+00:00"
"time": "2020-10-24T10:57:07+00:00"
},
{
"name": "webmozart/assert",
"version": "1.10.0",
"version": "1.9.1",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
"reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"php": "^5.3.3 || ^7.0 || ^8.0",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
"phpstan/phpstan": "<0.12.20",
"vimeo/psalm": "<4.6.1 || 4.6.2"
"vimeo/psalm": "<3.9.1"
},
"require-dev": {
"phpunit/phpunit": "^8.5.13"
"phpunit/phpunit": "^4.8.36 || ^7.5.13"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.10-dev"
}
},
"autoload": {
"psr-4": {
"Webmozart\\Assert\\": "src/"
@@ -1617,7 +1750,11 @@
"check",
"validate"
],
"time": "2021-03-09T10:59:23+00:00"
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/1.9.1"
},
"time": "2020-07-08T17:02:28+00:00"
}
],
"aliases": [],
@@ -1629,5 +1766,5 @@
"php": "^5.6.0 || ^7.0 || ^8.0"
},
"platform-dev": [],
"plugin-api-version": "1.1.0"
"plugin-api-version": "2.3.0"
}

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,12 +182,12 @@
"Use Current Timezone": "Impiegà u fusu orariu attuale",
"Convert To UTC": "Cunvertisce in UTC",
"Close": "Chjode",
"Encrypted note on PrivateBin": "Nota cifrata nantà PrivateBin",
"Encrypted note on %s": "Nota cifrata nantà %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitate stu liame per vede a nota. Date lindirizzu à qualunque li permette daccede à a nota dinù.",
"URL shortener may expose your decrypt key in URL.": "Un ammuzzatore dindirizzu pò palisà a vostra chjave di dicifratura in lindirizzu.",
"Save paste": "Arregistrà lappiccicu",
"Your IP is not authorized to create pastes.": "U vostru indirizzu IP ùn hè micca auturizatu à creà lappiccichi.",
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
"Error parsing YOURLS response.": "Error parsing YOURLS response."
"Trying to shorten a URL that isn't pointing at our instance.": "Pruvate dammuzzà un indirizzu web chì ùn punta micca versu a vostra instanza.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Sbagliu à a chjama di YOURLS. Seria forse una cunfigurazione gattiva, tale una \"apiurl\" o \"signature\" falsa o assente.",
"Error parsing YOURLS response.": "Sbagliu durante lanalisa di a risposta di YOURLS."
}

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Použít aktuální časové pásmo",
"Convert To UTC": "Převést na UTC",
"Close": "Zavřít",
"Encrypted note on PrivateBin": "Šifrovaná poznámka ve službě PrivateBin",
"Encrypted note on %s": "Šifrovaná poznámka ve službě %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Navštivte tento odkaz pro zobrazení poznámky. Přeposláním URL umožníte také jiným lidem přístup.",
"URL shortener may expose your decrypt key in URL.": "Zkracovač URL může odhalit váš dešifrovací klíč v URL.",
"Save paste": "Uložit příspěvek",

View File

@@ -182,12 +182,12 @@
"Use Current Timezone": "Aktuelle Zeitzone verwenden",
"Convert To UTC": "In UTC umwandeln",
"Close": "Schliessen",
"Encrypted note on PrivateBin": "Verschlüsselte Notiz auf PrivateBin",
"Encrypted note on %s": "Verschlüsselte Notiz auf %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besuche diesen Link um das Dokument zu sehen. Wird die URL an eine andere Person gegeben, so kann diese Person ebenfalls auf dieses Dokument zugreifen.",
"URL shortener may expose your decrypt key in URL.": "Der URL-Verkürzer kann den Schlüssel in der URL enthüllen.",
"Save paste": "Text speichern",
"Your IP is not authorized to create pastes.": "Deine IP ist nicht berechtigt, Texte zu erstellen.",
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
"Error parsing YOURLS response.": "Error parsing YOURLS response."
"Trying to shorten a URL that isn't pointing at our instance.": "Versuch eine URL zu verkürzen, die nicht auf unsere Instanz zeigt.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Fehler beim Aufruf von YOURLS. Wahrscheinlich ein Konfigurationsproblem, wie eine falsche oder fehlende \"apiurl\" oder \"signature\".",
"Error parsing YOURLS response.": "Fehler beim Verarbeiten der YOURLS-Antwort."
}

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Χρήση τρέχουσας ζώνης ώρας",
"Convert To UTC": "Μετατροπή σε UTC",
"Close": "Κλείσιμο",
"Encrypted note on PrivateBin": "Κρυπτογραφημένο μήνυμα από το PrivateBin",
"Encrypted note on %s": "Κρυπτογραφημένο μήνυμα από το %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Επισκεφτείτε αυτόν τον σύνδεσμο για να δείτε το μήνυμα. Δίνοντας τον σύνδεσμο σε οποιονδήποτε, του επιτρέπετε να επισκεφτεί το μήνυμα επίσης.",
"URL shortener may expose your decrypt key in URL.": "Συντομευτές συνδέσμων πιθανώς να δημοσιοποιήσουν το κλειδί αποκρυπτογράφισης στον σύνδεσμο.",
"Save paste": "Αποθήκευση επικόλλησης",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Usar Zona Horaria Actual",
"Convert To UTC": "Convertir A UTC",
"Close": "Cerrar",
"Encrypted note on PrivateBin": "Nota cifrada en PrivateBin",
"Encrypted note on %s": "Nota cifrada en %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite este enlace para ver la nota. Dar la URL a cualquier persona también les permite acceder a la nota.",
"URL shortener may expose your decrypt key in URL.": "El acortador de URL puede exponer su clave de descifrado en el URL.",
"Save paste": "Guardar \"paste\"",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Kasuta praegust ajavööndit",
"Convert To UTC": "Teisenda UTC-ks",
"Close": "Sulge",
"Encrypted note on PrivateBin": "Krüpteeritud kiri PrivateBin-is",
"Encrypted note on %s": "Krüpteeritud kiri %s-is",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kirja nägemiseks külasta seda linki. Teistele URL-i andmine lubab ka neil ligi pääseda kirjale.",
"URL shortener may expose your decrypt key in URL.": "URL-i lühendaja võib paljastada sinu dekrüpteerimisvõtme URL-is.",
"Save paste": "Salvesta kleebe",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Käytä nykyistä aikavyöhykettä",
"Convert To UTC": "Muuta UTC:ksi",
"Close": "Sulje",
"Encrypted note on PrivateBin": "Salattu viesti PrivateBinissä",
"Encrypted note on %s": "Salattu viesti %sissä",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Käy tässä linkissä nähdäksesi viestin. URL:n antaminen kenellekään antaa heidänkin päästä katsomeen viestiä. ",
"URL shortener may expose your decrypt key in URL.": "URL-lyhentäjä voi paljastaa purkuavaimesi URL:ssä.",
"Save paste": "Tallenna paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Conserver l'actuel",
"Convert To UTC": "Convertir en UTC",
"Close": "Fermer",
"Encrypted note on PrivateBin": "Message chiffré sur PrivateBin",
"Encrypted note on %s": "Message chiffré sur %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visiter ce lien pour voir la note. Donner l'URL à une autre personne lui permet également d'accéder à la note.",
"URL shortener may expose your decrypt key in URL.": "Raccourcir l'URL peut exposer votre clé de déchiffrement dans l'URL.",
"Save paste": "Sauver le paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "להשתמש באזור הזמן הנוכחי",
"Convert To UTC": "המרה ל־UTC",
"Close": "סגירה",
"Encrypted note on PrivateBin": "הערה מוצפנת ב־PrivateBin",
"Encrypted note on %s": "%sהערה מוצפנת ב־",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "נא לבקר בקישור כדי לצפות בהערה. מסירת הקישור לאנשים כלשהם תאפשר גם להם לגשת להערה.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Az aktuális időzóna használata",
"Convert To UTC": "Átalakítás UTC időzónára",
"Close": "Bezárás",
"Encrypted note on PrivateBin": "Titkosított jegyzet a PrivateBinen",
"Encrypted note on %s": "Titkosított jegyzet a %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Látogasd meg ezt a hivatkozást a bejegyzés megtekintéséhez. Ha mások számára is megadod ezt a linket, azzal hozzáférnek ők is.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Gunakan Zonawaktu Saat Ini",
"Convert To UTC": "Konversi Ke UTC",
"Close": "Tutup",
"Encrypted note on PrivateBin": "Catatan ter-ekrip di PrivateBin",
"Encrypted note on %s": "Catatan ter-ekrip di %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Kunjungi tautan ini untuk melihat catatan. Memberikan alamat URL pada siapapun juga, akan mengizinkan mereka untuk mengakses catatan, so pasti gitu loh Kaka.",
"URL shortener may expose your decrypt key in URL.": "Pemendek URL mungkin akan menampakkan kunci dekrip Anda dalam URL.",
"Save paste": "Simpan paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Usa Fuso Orario Corrente",
"Convert To UTC": "Converti a UTC",
"Close": "Chiudi",
"Encrypted note on PrivateBin": "Nota crittografata su PrivateBin",
"Encrypted note on %s": "Nota crittografata su %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visita questo collegamento per vedere la nota. Dare l'URL a chiunque consente anche a loro di accedere alla nota.",
"URL shortener may expose your decrypt key in URL.": "URL shortener può esporre la tua chiave decrittografata nell'URL.",
"Save paste": "Salva il messagio",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "galfi lo cabni la utc",
"Close": "ganlo",
"Encrypted note on PrivateBin": ".i lo lo notci ku mifra cu zvati sivlolnitvanku'a",
"Encrypted note on %s": ".i lo lo notci ku mifra cu zvati %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "rejgau fukpi",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Naudoti esamą laiko juostą",
"Convert To UTC": "Konvertuoti į UTC",
"Close": "Užverti",
"Encrypted note on PrivateBin": "Šifruoti užrašai ties PrivateBin",
"Encrypted note on %s": "Šifruoti užrašai ties %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Norėdami matyti užrašus, aplankykite šį tinklalapį. Pasidalinus šiuo URL adresu su kitais žmonėmis, jiems taip pat bus leidžiama prieiga prie šių užrašų.",
"URL shortener may expose your decrypt key in URL.": "URL trumpinimo įrankis gali atskleisti URL adrese jūsų iššifravimo raktą.",
"Save paste": "Įrašyti įdėjimą",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Gebruik huidige tijdzone",
"Convert To UTC": "Omzetten naar UTC",
"Close": "Sluiten",
"Encrypted note on PrivateBin": "Versleutelde notitie op PrivateBin",
"Encrypted note on %s": "Versleutelde notitie op %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Bezoek deze link om de notitie te bekijken. Als je de URL aan iemand geeft, kan die de notitie ook bekijken.",
"URL shortener may expose your decrypt key in URL.": "URL-verkorter kan uw ontcijferingssleutel in URL blootleggen.",
"Save paste": "Notitie opslaan",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Bruk gjeldende tidssone",
"Convert To UTC": "Konverter til UTC",
"Close": "Steng",
"Encrypted note on PrivateBin": "Kryptert notat på PrivateBin",
"Encrypted note on %s": "Kryptert notat på %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Besøk denne lenken for å se notatet. Hvis lenken deles med andre, vil de også kunne se notatet.",
"URL shortener may expose your decrypt key in URL.": "URL forkorter kan avsløre dekrypteringsnøkkelen.",
"Save paste": "Lagre utklipp",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Utilizar lactual",
"Convert To UTC": "Convertir en UTC",
"Close": "Tampar",
"Encrypted note on PrivateBin": "Nòtas chifradas sus PrivateBin",
"Encrypted note on %s": "Nòtas chifradas sus %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visitatz aqueste ligam per veire la nòta. Fornir lo ligam a qualquun mai li permet tanben daccedir a la nòta.",
"URL shortener may expose your decrypt key in URL.": "Los espleches dacorchiment dURL pòdon expausar la clau de deschiframent dins lURL.",
"Save paste": "Enregistrar lo tèxt",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Usar Fuso Horário Atual",
"Convert To UTC": "Converter para UTC",
"Close": "Fechar",
"Encrypted note on PrivateBin": "Nota criptografada no PrivateBin",
"Encrypted note on %s": "Nota criptografada no %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visite esse link para ver a nota. Dar a URL para qualquer um permite que eles também acessem a nota.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Использовать текущий часовой пояс",
"Convert To UTC": "Конвертировать в UTC",
"Close": "Закрыть",
"Encrypted note on PrivateBin": "Зашифрованная запись на PrivateBin",
"Encrypted note on %s": "Зашифрованная запись на %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Посетите эту ссылку чтобы просмотреть запись. Передача ссылки кому либо позволит им получить доступ к записи тоже.",
"URL shortener may expose your decrypt key in URL.": "Сервис сокращения ссылок может получить ваш ключ расшифровки из ссылки.",
"Save paste": "Сохранить запись",

View File

@@ -182,12 +182,12 @@
"Use Current Timezone": "Použiť aktuálne časové pásmo",
"Convert To UTC": "Previesť na UTC",
"Close": "Zavrieť",
"Encrypted note on PrivateBin": "Zašifrovaná poznámka na PrivateBin",
"Encrypted note on %s": "Zašifrovaná poznámka na %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Ak chcete zobraziť poznámku, navštívte tento odkaz. Poskytnutie adresy URL komukoľvek im umožní prístup aj k poznámke.",
"URL shortener may expose your decrypt key in URL.": "Skracovač adries URL môže odhaliť váš dešifrovací kľúč v adrese URL.",
"Save paste": "Uložiť príspevok",
"Your IP is not authorized to create pastes.": "Vaša IP adresa nie je oprávnená vytvárať príspevky.",
"Trying to shorten a URL that isn't pointing at our instance.": "Trying to shorten a URL that isn't pointing at our instance.",
"Trying to shorten a URL that isn't pointing at our instance.": "Pokúšate sa skrátiť adresu URL, ktorá neukazuje na túto inštanciu.",
"Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".": "Error calling YOURLS. Probably a configuration issue, like wrong or missing \"apiurl\" or \"signature\".",
"Error parsing YOURLS response.": "Error parsing YOURLS response."
}

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Use Current Timezone",
"Convert To UTC": "Convert To UTC",
"Close": "Close",
"Encrypted note on PrivateBin": "Encrypted note on PrivateBin",
"Encrypted note on %s": "Encrypted note on %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.",
"URL shortener may expose your decrypt key in URL.": "URL shortener may expose your decrypt key in URL.",
"Save paste": "Save paste",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Şuanki zaman dilimini kullan",
"Convert To UTC": "UTC zaman dilimine çevir",
"Close": "Kapat",
"Encrypted note on PrivateBin": "PrivateBin üzerinde şifrelenmiş not",
"Encrypted note on %s": "%s üzerinde şifrelenmiş not",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Notu görmek için bu bağlantıyı ziyaret et. Bağlantıya sahip olan birisi notu görebilir.",
"URL shortener may expose your decrypt key in URL.": "URL kısaltıcı şifreleme anahtarınızı URL içerisinde gösterebilir.",
"Save paste": "Yazıyı kaydet",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "Використовувати поточний часовий пояс",
"Convert To UTC": "Конвертувати в UTC",
"Close": "Закрити",
"Encrypted note on PrivateBin": "Зашифрована нотатка на PrivateBin",
"Encrypted note on %s": "Зашифрована нотатка на %s",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "Відвідайте посилання, щоб переглянути нотатку. Передача посилання будь-кому дозволить їм переглянути нотатку.",
"URL shortener may expose your decrypt key in URL.": "Сервіс скорочення посилань може викрити ваш ключ дешифрування з URL.",
"Save paste": "Зберегти вставку",

View File

@@ -182,7 +182,7 @@
"Use Current Timezone": "使用当前时区",
"Convert To UTC": "转换为 UTC",
"Close": "关闭",
"Encrypted note on PrivateBin": "PrivateBin 上的加密笔记",
"Encrypted note on %s": "%s 上的加密笔记",
"Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.": "访问此链接来查看该笔记。将此 URL 发送给任何人即可允许其访问该笔记。",
"URL shortener may expose your decrypt key in URL.": "短链接服务可能会暴露您在 URL 中的解密密钥。",
"Save paste": "保存内容",

View File

@@ -584,7 +584,7 @@ class Database extends AbstractData
// workaround for https://bugs.php.net/bug.php?id=46728
$result = array();
while ($row = $statement->fetch(PDO::FETCH_ASSOC)) {
$result[] = array_map('self::_sanitizeClob', $row);
$result[] = array_map('PrivateBin\Data\Database::_sanitizeClob', $row);
}
} else {
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
@@ -593,7 +593,7 @@ class Database extends AbstractData
if (self::$_type === 'oci' && is_array($result)) {
// returned CLOB values are streams, convert these into strings
$result = $firstOnly ?
array_map('self::_sanitizeClob', $result) :
array_map('PrivateBin\Data\Database::_sanitizeClob', $result) :
$result;
}
return $result;

View File

@@ -351,7 +351,7 @@ class Filesystem extends AbstractData
$pastes = array();
$firstLevel = array_filter(
scandir(self::$_path),
'self::_isFirstLevelDir'
'PrivateBin\Data\Filesystem::_isFirstLevelDir'
);
if (count($firstLevel) > 0) {
// try at most 10 times the $batchsize pastes before giving up
@@ -359,7 +359,7 @@ class Filesystem extends AbstractData
$firstKey = array_rand($firstLevel);
$secondLevel = array_filter(
scandir(self::$_path . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
'self::_isSecondLevelDir'
'PrivateBin\Data\Filesystem::_isSecondLevelDir'
);
// skip this folder in the next checks if it is empty

View File

@@ -84,7 +84,7 @@ class I18n
*/
public static function _($messageId)
{
return forward_static_call_array('self::translate', func_get_args());
return forward_static_call_array('PrivateBin\I18n::translate', func_get_args());
}
/**

View File

@@ -14,6 +14,7 @@ namespace PrivateBin\Model;
use Exception;
use Identicon\Identicon;
use Jdenticon\Identicon as Jdenticon;
use PrivateBin\Persistence\TrafficLimiter;
use PrivateBin\Vizhash16x16;
@@ -167,6 +168,16 @@ class Comment extends AbstractModel
if ($icon == 'identicon') {
$identicon = new Identicon();
$pngdata = $identicon->getImageDataUri($hmac, 16);
} elseif ($icon == 'jdenticon') {
$jdenticon = new Jdenticon(array(
'hash' => $hmac,
'size' => 16,
'style' => array(
'backgroundColor' => '#fff0', // fully transparent, for dark mode
'padding' => 0,
),
));
$pngdata = $jdenticon->getImageDataUri('png');
} elseif ($icon == 'vizhash') {
$vh = new Vizhash16x16();
$pngdata = 'data:image/png;base64,' . base64_encode(

View File

@@ -85,7 +85,7 @@ endif;
<meta name="theme-color" content="#ffe57e" />
<!-- Twitter/social media cards -->
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="<?php echo I18n::_('Encrypted note on PrivateBin') ?>" />
<meta name="twitter:title" content="<?php echo I18n::_('Encrypted note on %s', I18n::_($NAME)) ?>" />
<meta name="twitter:description" content="<?php echo I18n::_('Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.') ?>" />
<meta name="twitter:image" content="<?php echo I18n::encode($BASEPATH); ?>img/apple-touch-icon.png" />
<meta property="og:title" content="<?php echo I18n::_($NAME); ?>" />

View File

@@ -63,7 +63,7 @@ endif;
<meta name="theme-color" content="#ffe57e" />
<!-- Twitter/social media cards -->
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="<?php echo I18n::_('Encrypted note on PrivateBin') ?>" />
<meta name="twitter:title" content="<?php echo I18n::_('Encrypted note on %s', I18n::_($NAME)) ?>" />
<meta name="twitter:description" content="<?php echo I18n::_('Visit this link to see the note. Giving the URL to anyone allows them to access the note, too.') ?>" />
<meta name="twitter:image" content="img/apple-touch-icon.png?<?php echo rawurlencode($VERSION); ?>" />
<meta property="og:title" content="<?php echo I18n::_($NAME); ?>" />

View File

@@ -9,6 +9,7 @@ use Identicon\Generator\GdGenerator;
use Identicon\Generator\ImageMagickGenerator;
use Identicon\Generator\SvgGenerator;
use Identicon\Identicon;
use Jdenticon\Identicon as Jdenticon;
use PrivateBin\Vizhash16x16;
@@ -17,7 +18,19 @@ $vizhash = new Vizhash16x16();
$identiconGenerators = array(
'identicon GD' => new Identicon(new GdGenerator()),
'identicon ImageMagick' => new Identicon(new ImageMagickGenerator()),
'identicon SVG' => new Identicon(new SvgGenerator())
'identicon SVG' => new Identicon(new SvgGenerator()),
);
$jdenticon = new Jdenticon(array(
'size' => 16,
'style' => array(
'backgroundColor' => '#fff0', // fully transparent, for dark mode
'padding' => 0,
),
));
$jdenticonGenerators = array(
'jdenticon' => 'png',
'jdenticon ImageMagick' => 'png',
'jdenticon SVG' => 'svg',
);
$results = array(
'vizhash' => array(
@@ -35,20 +48,29 @@ $results = array(
'identicon SVG' => array(
'lengths' => array(),
'time' => 0
)
),
'jdenticon' => array(
'lengths' => array(),
'time' => 0
),
'jdenticon ImageMagick' => array(
'lengths' => array(),
'time' => 0
),
'jdenticon SVG' => array(
'lengths' => array(),
'time' => 0
),
);
$hmacs = array();
echo 'generate ', ITERATIONS, ' hmacs and pre-populate the result array, so tests wont be slowed down', PHP_EOL;
for ($i = 0; $i < ITERATIONS; ++$i) {
$hmacs[$i] = hash_hmac('sha512', '127.0.0.1', bin2hex(random_bytes(256)));
$results['vizhash']['lengths'][$i] = 0;
$results['identicon GD']['lengths'][$i] = 0;
$results['identicon ImageMagick']['lengths'][$i] = 0;
$results['identicon SVG']['lengths'][$i] = 0;
foreach (array_keys($results) as $test) {
$results[$test]['lengths'][$i] = 0;
}
}
echo 'run vizhash tests', PHP_EOL;
$start = microtime(true);
@@ -60,7 +82,6 @@ foreach ($hmacs as $i => $hmac) {
}
$results['vizhash']['time'] = microtime(true) - $start;
foreach ($identiconGenerators as $key => $identicon) {
echo 'run ', $key,' tests', PHP_EOL;
$start = microtime(true);
@@ -71,9 +92,35 @@ foreach ($identiconGenerators as $key => $identicon) {
$results[$key]['time'] = microtime(true) - $start;
}
foreach ($jdenticonGenerators as $key => $format) {
echo 'run ', $key,' tests', PHP_EOL;
if ($key === 'jdenticon ImageMagick') {
$jdenticon->enableImageMagick = true;
} else {
$jdenticon->enableImageMagick = false;
}
$start = microtime(true);
foreach ($hmacs as $i => $hmac) {
$jdenticon->setHash($hmac);
$data = $jdenticon->getImageDataUri($format);
$results[$key]['lengths'][$i] = strlen($data);
}
$results[$key]['time'] = microtime(true) - $start;
}
define('PADDING_LENGTH', max(array_map(function ($key) { return strlen($key); }, array_keys($results))) + 1);
define(
'PADDING_LENGTH',
max(
array_map(
function ($key) {
return strlen($key);
},
array_keys($results)
)
) + 1
);
function format_result_line($generator, $min, $max, $avg, $sec) {
echo str_pad($generator, PADDING_LENGTH, ' '), "\t",
str_pad($min, 4, ' ', STR_PAD_LEFT), "\t",
@@ -84,7 +131,10 @@ function format_result_line($generator, $min, $max, $avg, $sec) {
echo PHP_EOL;
format_result_line('Generator:', 'min', 'max', 'avg', 'seconds');
format_result_line(str_repeat('─', PADDING_LENGTH), str_repeat('─', 4), str_repeat('─', 4), str_repeat('─', 4), str_repeat('─', 7));
format_result_line(
str_repeat('─', PADDING_LENGTH), str_repeat('─', 4), str_repeat('─', 4),
str_repeat('─', 4), str_repeat('─', 7)
);
foreach ($results as $generator => $result) {
sort($result['lengths']);
format_result_line(

View File

@@ -37,57 +37,130 @@ namespace Composer\Autoload;
*
* @author Fabien Potencier <fabien@symfony.com>
* @author Jordi Boggiano <j.boggiano@seld.be>
* @see http://www.php-fig.org/psr/psr-0/
* @see http://www.php-fig.org/psr/psr-4/
* @see https://www.php-fig.org/psr/psr-0/
* @see https://www.php-fig.org/psr/psr-4/
*/
class ClassLoader
{
/** @var ?string */
private $vendorDir;
// PSR-4
/**
* @var array[]
* @psalm-var array<string, array<string, int>>
*/
private $prefixLengthsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, array<int, string>>
*/
private $prefixDirsPsr4 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr4 = array();
// PSR-0
/**
* @var array[]
* @psalm-var array<string, array<string, string[]>>
*/
private $prefixesPsr0 = array();
/**
* @var array[]
* @psalm-var array<string, string>
*/
private $fallbackDirsPsr0 = array();
/** @var bool */
private $useIncludePath = false;
/**
* @var string[]
* @psalm-var array<string, string>
*/
private $classMap = array();
/** @var bool */
private $classMapAuthoritative = false;
/**
* @var bool[]
* @psalm-var array<string, bool>
*/
private $missingClasses = array();
/** @var ?string */
private $apcuPrefix;
/**
* @var self[]
*/
private static $registeredLoaders = array();
/**
* @param ?string $vendorDir
*/
public function __construct($vendorDir = null)
{
$this->vendorDir = $vendorDir;
}
/**
* @return string[]
*/
public function getPrefixes()
{
if (!empty($this->prefixesPsr0)) {
return call_user_func_array('array_merge', $this->prefixesPsr0);
return call_user_func_array('array_merge', array_values($this->prefixesPsr0));
}
return array();
}
/**
* @return array[]
* @psalm-return array<string, array<int, string>>
*/
public function getPrefixesPsr4()
{
return $this->prefixDirsPsr4;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirs()
{
return $this->fallbackDirsPsr0;
}
/**
* @return array[]
* @psalm-return array<string, string>
*/
public function getFallbackDirsPsr4()
{
return $this->fallbackDirsPsr4;
}
/**
* @return string[] Array of classname => path
* @psalm-return array<string, string>
*/
public function getClassMap()
{
return $this->classMap;
}
/**
* @param array $classMap Class to filename map
* @param string[] $classMap Class to filename map
* @psalm-param array<string, string> $classMap
*
* @return void
*/
public function addClassMap(array $classMap)
{
@@ -103,8 +176,10 @@ class ClassLoader
* appending or prepending to the ones previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 root directories
* @param string[]|string $paths The PSR-0 root directories
* @param bool $prepend Whether to prepend the directories
*
* @return void
*/
public function add($prefix, $paths, $prepend = false)
{
@@ -148,10 +223,12 @@ class ClassLoader
* appending or prepending to the ones previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string[]|string $paths The PSR-4 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@@ -196,7 +273,9 @@ class ClassLoader
* replacing any others previously set for this prefix.
*
* @param string $prefix The prefix
* @param array|string $paths The PSR-0 base directories
* @param string[]|string $paths The PSR-0 base directories
*
* @return void
*/
public function set($prefix, $paths)
{
@@ -212,9 +291,11 @@ class ClassLoader
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string[]|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*
* @return void
*/
public function setPsr4($prefix, $paths)
{
@@ -234,6 +315,8 @@ class ClassLoader
* Turns on searching the include path for class files.
*
* @param bool $useIncludePath
*
* @return void
*/
public function setUseIncludePath($useIncludePath)
{
@@ -256,6 +339,8 @@ class ClassLoader
* that have not been registered with the class map.
*
* @param bool $classMapAuthoritative
*
* @return void
*/
public function setClassMapAuthoritative($classMapAuthoritative)
{
@@ -276,6 +361,8 @@ class ClassLoader
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
*
* @param string|null $apcuPrefix
*
* @return void
*/
public function setApcuPrefix($apcuPrefix)
{
@@ -296,25 +383,44 @@ class ClassLoader
* Registers this instance as an autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not
*
* @return void
*/
public function register($prepend = false)
{
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
if (null === $this->vendorDir) {
return;
}
if ($prepend) {
self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders;
} else {
unset(self::$registeredLoaders[$this->vendorDir]);
self::$registeredLoaders[$this->vendorDir] = $this;
}
}
/**
* Unregisters this instance as an autoloader.
*
* @return void
*/
public function unregister()
{
spl_autoload_unregister(array($this, 'loadClass'));
if (null !== $this->vendorDir) {
unset(self::$registeredLoaders[$this->vendorDir]);
}
}
/**
* Loads the given class or interface.
*
* @param string $class The name of the class
* @return bool|null True if loaded, null otherwise
* @return true|null True if loaded, null otherwise
*/
public function loadClass($class)
{
@@ -323,6 +429,8 @@ class ClassLoader
return true;
}
return null;
}
/**
@@ -367,6 +475,21 @@ class ClassLoader
return $file;
}
/**
* Returns the currently registered loaders indexed by their corresponding vendor directories.
*
* @return self[]
*/
public static function getRegisteredLoaders()
{
return self::$registeredLoaders;
}
/**
* @param string $class
* @param string $ext
* @return string|false
*/
private function findFileWithExtension($class, $ext)
{
// PSR-4 lookup
@@ -438,6 +561,10 @@ class ClassLoader
* Scope isolated include.
*
* Prevents access to $this/self from included files.
*
* @param string $file
* @return void
* @private
*/
function includeFile($file)
{

350
vendor/composer/InstalledVersions.php vendored Normal file
View File

@@ -0,0 +1,350 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer;
use Composer\Autoload\ClassLoader;
use Composer\Semver\VersionParser;
/**
* This class is copied in every Composer installed project and available to all
*
* See also https://getcomposer.org/doc/07-runtime.md#installed-versions
*
* To require its presence, you can require `composer-runtime-api ^2.0`
*/
class InstalledVersions
{
/**
* @var mixed[]|null
* @psalm-var array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}|array{}|null
*/
private static $installed;
/**
* @var bool|null
*/
private static $canGetVendors;
/**
* @var array[]
* @psalm-var array<string, array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static $installedByVendor = array();
/**
* Returns a list of all package names which are present, either by being installed, replaced or provided
*
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackages()
{
$packages = array();
foreach (self::getInstalled() as $installed) {
$packages[] = array_keys($installed['versions']);
}
if (1 === \count($packages)) {
return $packages[0];
}
return array_keys(array_flip(\call_user_func_array('array_merge', $packages)));
}
/**
* Returns a list of all package names with a specific type e.g. 'library'
*
* @param string $type
* @return string[]
* @psalm-return list<string>
*/
public static function getInstalledPackagesByType($type)
{
$packagesByType = array();
foreach (self::getInstalled() as $installed) {
foreach ($installed['versions'] as $name => $package) {
if (isset($package['type']) && $package['type'] === $type) {
$packagesByType[] = $name;
}
}
}
return $packagesByType;
}
/**
* Checks whether the given package is installed
*
* This also returns true if the package name is provided or replaced by another package
*
* @param string $packageName
* @param bool $includeDevRequirements
* @return bool
*/
public static function isInstalled($packageName, $includeDevRequirements = true)
{
foreach (self::getInstalled() as $installed) {
if (isset($installed['versions'][$packageName])) {
return $includeDevRequirements || empty($installed['versions'][$packageName]['dev_requirement']);
}
}
return false;
}
/**
* Checks whether the given package satisfies a version constraint
*
* e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call:
*
* Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3')
*
* @param VersionParser $parser Install composer/semver to have access to this class and functionality
* @param string $packageName
* @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package
* @return bool
*/
public static function satisfies(VersionParser $parser, $packageName, $constraint)
{
$constraint = $parser->parseConstraints($constraint);
$provided = $parser->parseConstraints(self::getVersionRanges($packageName));
return $provided->matches($constraint);
}
/**
* Returns a version constraint representing all the range(s) which are installed for a given package
*
* It is easier to use this via isInstalled() with the $constraint argument if you need to check
* whether a given version of a package is installed, and not just whether it exists
*
* @param string $packageName
* @return string Version constraint usable with composer/semver
*/
public static function getVersionRanges($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
$ranges = array();
if (isset($installed['versions'][$packageName]['pretty_version'])) {
$ranges[] = $installed['versions'][$packageName]['pretty_version'];
}
if (array_key_exists('aliases', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']);
}
if (array_key_exists('replaced', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']);
}
if (array_key_exists('provided', $installed['versions'][$packageName])) {
$ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']);
}
return implode(' || ', $ranges);
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['version'])) {
return null;
}
return $installed['versions'][$packageName]['version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present
*/
public static function getPrettyVersion($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['pretty_version'])) {
return null;
}
return $installed['versions'][$packageName]['pretty_version'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference
*/
public static function getReference($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
if (!isset($installed['versions'][$packageName]['reference'])) {
return null;
}
return $installed['versions'][$packageName]['reference'];
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @param string $packageName
* @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path.
*/
public static function getInstallPath($packageName)
{
foreach (self::getInstalled() as $installed) {
if (!isset($installed['versions'][$packageName])) {
continue;
}
return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null;
}
throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed');
}
/**
* @return array
* @psalm-return array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}
*/
public static function getRootPackage()
{
$installed = self::getInstalled();
return $installed[0]['root'];
}
/**
* Returns the raw installed.php data for custom implementations
*
* @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect.
* @return array[]
* @psalm-return array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}
*/
public static function getRawData()
{
@trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED);
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = include __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
return self::$installed;
}
/**
* Returns the raw data of all installed.php which are currently loaded for custom implementations
*
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
public static function getAllRawData()
{
return self::getInstalled();
}
/**
* Lets you reload the static array from another file
*
* This is only useful for complex integrations in which a project needs to use
* this class but then also needs to execute another project's autoloader in process,
* and wants to ensure both projects have access to their version of installed.php.
*
* A typical case would be PHPUnit, where it would need to make sure it reads all
* the data it needs from this class, then call reload() with
* `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure
* the project in which it runs can then also use this class safely, without
* interference between PHPUnit's dependencies and the project's dependencies.
*
* @param array[] $data A vendor/composer/installed.php data set
* @return void
*
* @psalm-param array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>} $data
*/
public static function reload($data)
{
self::$installed = $data;
self::$installedByVendor = array();
}
/**
* @return array[]
* @psalm-return list<array{root: array{name: string, version: string, reference: string, pretty_version: string, aliases: string[], dev: bool, install_path: string, type: string}, versions: array<string, array{dev_requirement: bool, pretty_version?: string, version?: string, aliases?: string[], reference?: string, replaced?: string[], provided?: string[], install_path?: string, type?: string}>}>
*/
private static function getInstalled()
{
if (null === self::$canGetVendors) {
self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders');
}
$installed = array();
if (self::$canGetVendors) {
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
if (isset(self::$installedByVendor[$vendorDir])) {
$installed[] = self::$installedByVendor[$vendorDir];
} elseif (is_file($vendorDir.'/composer/installed.php')) {
$installed[] = self::$installedByVendor[$vendorDir] = require $vendorDir.'/composer/installed.php';
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
self::$installed = $installed[count($installed) - 1];
}
}
}
}
if (null === self::$installed) {
// only require the installed.php file if this file is loaded from its dumped location,
// and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937
if (substr(__DIR__, -8, 1) !== 'C') {
self::$installed = require __DIR__ . '/installed.php';
} else {
self::$installed = array();
}
}
$installed[] = self::$installed;
return $installed;
}
}

View File

@@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php',
'IPLib\\Address\\AddressInterface' => $vendorDir . '/mlocati/ip-lib/src/Address/AddressInterface.php',
'IPLib\\Address\\AssignedRange' => $vendorDir . '/mlocati/ip-lib/src/Address/AssignedRange.php',
'IPLib\\Address\\IPv4' => $vendorDir . '/mlocati/ip-lib/src/Address/IPv4.php',
@@ -28,12 +29,49 @@ return array(
'Identicon\\Generator\\ImageMagickGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/ImageMagickGenerator.php',
'Identicon\\Generator\\SvgGenerator' => $vendorDir . '/yzalis/identicon/src/Identicon/Generator/SvgGenerator.php',
'Identicon\\Identicon' => $vendorDir . '/yzalis/identicon/src/Identicon/Identicon.php',
'Jdenticon\\Canvas\\Canvas' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Canvas.php',
'Jdenticon\\Canvas\\CanvasContext' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/CanvasContext.php',
'Jdenticon\\Canvas\\ColorUtils' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/ColorUtils.php',
'Jdenticon\\Canvas\\Matrix' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Matrix.php',
'Jdenticon\\Canvas\\Png\\PngBuffer' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Png/PngBuffer.php',
'Jdenticon\\Canvas\\Png\\PngEncoder' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Png/PngEncoder.php',
'Jdenticon\\Canvas\\Png\\PngPalette' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Png/PngPalette.php',
'Jdenticon\\Canvas\\Point' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Point.php',
'Jdenticon\\Canvas\\Rasterization\\Edge' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/Edge.php',
'Jdenticon\\Canvas\\Rasterization\\EdgeIntersection' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/EdgeIntersection.php',
'Jdenticon\\Canvas\\Rasterization\\EdgeSuperSampleIntersection' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/EdgeSuperSampleIntersection.php',
'Jdenticon\\Canvas\\Rasterization\\EdgeTable' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/EdgeTable.php',
'Jdenticon\\Canvas\\Rasterization\\Layer' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/Layer.php',
'Jdenticon\\Canvas\\Rasterization\\LayerManager' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/LayerManager.php',
'Jdenticon\\Canvas\\Rasterization\\Rasterizer' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/Rasterizer.php',
'Jdenticon\\Canvas\\Rasterization\\SuperSampleBuffer' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/SuperSampleBuffer.php',
'Jdenticon\\Canvas\\Rasterization\\SuperSampleRange' => $vendorDir . '/jdenticon/jdenticon/src/Canvas/Rasterization/SuperSampleRange.php',
'Jdenticon\\Color' => $vendorDir . '/jdenticon/jdenticon/src/Color.php',
'Jdenticon\\Identicon' => $vendorDir . '/jdenticon/jdenticon/src/Identicon.php',
'Jdenticon\\IdenticonStyle' => $vendorDir . '/jdenticon/jdenticon/src/IdenticonStyle.php',
'Jdenticon\\Rendering\\AbstractRenderer' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/AbstractRenderer.php',
'Jdenticon\\Rendering\\ColorTheme' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/ColorTheme.php',
'Jdenticon\\Rendering\\IconGenerator' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/IconGenerator.php',
'Jdenticon\\Rendering\\ImagickRenderer' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/ImagickRenderer.php',
'Jdenticon\\Rendering\\InternalPngRenderer' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/InternalPngRenderer.php',
'Jdenticon\\Rendering\\Point' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/Point.php',
'Jdenticon\\Rendering\\Rectangle' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/Rectangle.php',
'Jdenticon\\Rendering\\RendererInterface' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/RendererInterface.php',
'Jdenticon\\Rendering\\SvgPath' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/SvgPath.php',
'Jdenticon\\Rendering\\SvgRenderer' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/SvgRenderer.php',
'Jdenticon\\Rendering\\Transform' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/Transform.php',
'Jdenticon\\Rendering\\TriangleDirection' => $vendorDir . '/jdenticon/jdenticon/src/Rendering/TriangleDirection.php',
'Jdenticon\\Shapes\\Shape' => $vendorDir . '/jdenticon/jdenticon/src/Shapes/Shape.php',
'Jdenticon\\Shapes\\ShapeCategory' => $vendorDir . '/jdenticon/jdenticon/src/Shapes/ShapeCategory.php',
'Jdenticon\\Shapes\\ShapeDefinitions' => $vendorDir . '/jdenticon/jdenticon/src/Shapes/ShapeDefinitions.php',
'Jdenticon\\Shapes\\ShapePosition' => $vendorDir . '/jdenticon/jdenticon/src/Shapes/ShapePosition.php',
'PrivateBin\\Configuration' => $baseDir . '/lib/Configuration.php',
'PrivateBin\\Controller' => $baseDir . '/lib/Controller.php',
'PrivateBin\\Data\\AbstractData' => $baseDir . '/lib/Data/AbstractData.php',
'PrivateBin\\Data\\Database' => $baseDir . '/lib/Data/Database.php',
'PrivateBin\\Data\\Filesystem' => $baseDir . '/lib/Data/Filesystem.php',
'PrivateBin\\Data\\GoogleCloudStorage' => $baseDir . '/lib/Data/GoogleCloudStorage.php',
'PrivateBin\\Data\\S3Storage' => $baseDir . '/lib/Data/S3Storage.php',
'PrivateBin\\Filter' => $baseDir . '/lib/Filter.php',
'PrivateBin\\FormatV2' => $baseDir . '/lib/FormatV2.php',
'PrivateBin\\I18n' => $baseDir . '/lib/I18n.php',
@@ -49,4 +87,5 @@ return array(
'PrivateBin\\Request' => $baseDir . '/lib/Request.php',
'PrivateBin\\View' => $baseDir . '/lib/View.php',
'PrivateBin\\Vizhash16x16' => $baseDir . '/lib/Vizhash16x16.php',
'PrivateBin\\YourlsProxy' => $baseDir . '/lib/YourlsProxy.php',
);

View File

@@ -7,6 +7,7 @@ $baseDir = dirname($vendorDir);
return array(
'PrivateBin\\' => array($baseDir . '/lib'),
'Jdenticon\\' => array($vendorDir . '/jdenticon/jdenticon/src'),
'Identicon\\' => array($vendorDir . '/yzalis/identicon/src/Identicon'),
'IPLib\\' => array($vendorDir . '/mlocati/ip-lib/src'),
);

View File

@@ -22,13 +22,15 @@ class ComposerAutoloaderInitDontChange
return self::$loader;
}
require __DIR__ . '/platform_check.php';
spl_autoload_register(array('ComposerAutoloaderInitDontChange', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
spl_autoload_unregister(array('ComposerAutoloaderInitDontChange', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInitDontChange::getInitializer($loader));
} else {
@@ -63,11 +65,16 @@ class ComposerAutoloaderInitDontChange
}
}
/**
* @param string $fileIdentifier
* @param string $file
* @return void
*/
function composerRequireDontChange($fileIdentifier, $file)
{
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
require $file;
}
}

View File

@@ -15,6 +15,10 @@ class ComposerStaticInitDontChange
array (
'PrivateBin\\' => 11,
),
'J' =>
array (
'Jdenticon\\' => 10,
),
'I' =>
array (
'Identicon\\' => 10,
@@ -27,6 +31,10 @@ class ComposerStaticInitDontChange
array (
0 => __DIR__ . '/../..' . '/lib',
),
'Jdenticon\\' =>
array (
0 => __DIR__ . '/..' . '/jdenticon/jdenticon/src',
),
'Identicon\\' =>
array (
0 => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon',
@@ -38,6 +46,7 @@ class ComposerStaticInitDontChange
);
public static $classMap = array (
'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
'IPLib\\Address\\AddressInterface' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/AddressInterface.php',
'IPLib\\Address\\AssignedRange' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/AssignedRange.php',
'IPLib\\Address\\IPv4' => __DIR__ . '/..' . '/mlocati/ip-lib/src/Address/IPv4.php',
@@ -60,12 +69,49 @@ class ComposerStaticInitDontChange
'Identicon\\Generator\\ImageMagickGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/ImageMagickGenerator.php',
'Identicon\\Generator\\SvgGenerator' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Generator/SvgGenerator.php',
'Identicon\\Identicon' => __DIR__ . '/..' . '/yzalis/identicon/src/Identicon/Identicon.php',
'Jdenticon\\Canvas\\Canvas' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Canvas.php',
'Jdenticon\\Canvas\\CanvasContext' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/CanvasContext.php',
'Jdenticon\\Canvas\\ColorUtils' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/ColorUtils.php',
'Jdenticon\\Canvas\\Matrix' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Matrix.php',
'Jdenticon\\Canvas\\Png\\PngBuffer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Png/PngBuffer.php',
'Jdenticon\\Canvas\\Png\\PngEncoder' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Png/PngEncoder.php',
'Jdenticon\\Canvas\\Png\\PngPalette' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Png/PngPalette.php',
'Jdenticon\\Canvas\\Point' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Point.php',
'Jdenticon\\Canvas\\Rasterization\\Edge' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/Edge.php',
'Jdenticon\\Canvas\\Rasterization\\EdgeIntersection' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/EdgeIntersection.php',
'Jdenticon\\Canvas\\Rasterization\\EdgeSuperSampleIntersection' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/EdgeSuperSampleIntersection.php',
'Jdenticon\\Canvas\\Rasterization\\EdgeTable' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/EdgeTable.php',
'Jdenticon\\Canvas\\Rasterization\\Layer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/Layer.php',
'Jdenticon\\Canvas\\Rasterization\\LayerManager' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/LayerManager.php',
'Jdenticon\\Canvas\\Rasterization\\Rasterizer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/Rasterizer.php',
'Jdenticon\\Canvas\\Rasterization\\SuperSampleBuffer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/SuperSampleBuffer.php',
'Jdenticon\\Canvas\\Rasterization\\SuperSampleRange' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Canvas/Rasterization/SuperSampleRange.php',
'Jdenticon\\Color' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Color.php',
'Jdenticon\\Identicon' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Identicon.php',
'Jdenticon\\IdenticonStyle' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/IdenticonStyle.php',
'Jdenticon\\Rendering\\AbstractRenderer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/AbstractRenderer.php',
'Jdenticon\\Rendering\\ColorTheme' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/ColorTheme.php',
'Jdenticon\\Rendering\\IconGenerator' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/IconGenerator.php',
'Jdenticon\\Rendering\\ImagickRenderer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/ImagickRenderer.php',
'Jdenticon\\Rendering\\InternalPngRenderer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/InternalPngRenderer.php',
'Jdenticon\\Rendering\\Point' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/Point.php',
'Jdenticon\\Rendering\\Rectangle' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/Rectangle.php',
'Jdenticon\\Rendering\\RendererInterface' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/RendererInterface.php',
'Jdenticon\\Rendering\\SvgPath' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/SvgPath.php',
'Jdenticon\\Rendering\\SvgRenderer' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/SvgRenderer.php',
'Jdenticon\\Rendering\\Transform' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/Transform.php',
'Jdenticon\\Rendering\\TriangleDirection' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Rendering/TriangleDirection.php',
'Jdenticon\\Shapes\\Shape' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Shapes/Shape.php',
'Jdenticon\\Shapes\\ShapeCategory' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Shapes/ShapeCategory.php',
'Jdenticon\\Shapes\\ShapeDefinitions' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Shapes/ShapeDefinitions.php',
'Jdenticon\\Shapes\\ShapePosition' => __DIR__ . '/..' . '/jdenticon/jdenticon/src/Shapes/ShapePosition.php',
'PrivateBin\\Configuration' => __DIR__ . '/../..' . '/lib/Configuration.php',
'PrivateBin\\Controller' => __DIR__ . '/../..' . '/lib/Controller.php',
'PrivateBin\\Data\\AbstractData' => __DIR__ . '/../..' . '/lib/Data/AbstractData.php',
'PrivateBin\\Data\\Database' => __DIR__ . '/../..' . '/lib/Data/Database.php',
'PrivateBin\\Data\\Filesystem' => __DIR__ . '/../..' . '/lib/Data/Filesystem.php',
'PrivateBin\\Data\\GoogleCloudStorage' => __DIR__ . '/../..' . '/lib/Data/GoogleCloudStorage.php',
'PrivateBin\\Data\\S3Storage' => __DIR__ . '/../..' . '/lib/Data/S3Storage.php',
'PrivateBin\\Filter' => __DIR__ . '/../..' . '/lib/Filter.php',
'PrivateBin\\FormatV2' => __DIR__ . '/../..' . '/lib/FormatV2.php',
'PrivateBin\\I18n' => __DIR__ . '/../..' . '/lib/I18n.php',
@@ -81,6 +127,7 @@ class ComposerStaticInitDontChange
'PrivateBin\\Request' => __DIR__ . '/../..' . '/lib/Request.php',
'PrivateBin\\View' => __DIR__ . '/../..' . '/lib/View.php',
'PrivateBin\\Vizhash16x16' => __DIR__ . '/../..' . '/lib/Vizhash16x16.php',
'PrivateBin\\YourlsProxy' => __DIR__ . '/../..' . '/lib/YourlsProxy.php',
);
public static function getInitializer(ClassLoader $loader)

59
vendor/composer/installed.php vendored Normal file
View File

@@ -0,0 +1,59 @@
<?php return array(
'root' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '78aa70e3ab9277172489f9d88f8fd08cc1d03c97',
'name' => 'privatebin/privatebin',
'dev' => false,
),
'versions' => array(
'jdenticon/jdenticon' => array(
'pretty_version' => '1.0.1',
'version' => '1.0.1.0',
'type' => 'library',
'install_path' => __DIR__ . '/../jdenticon/jdenticon',
'aliases' => array(),
'reference' => '994ee07293fb978f983393ffcb2c0250592a6ac4',
'dev_requirement' => false,
),
'mlocati/ip-lib' => array(
'pretty_version' => '1.18.0',
'version' => '1.18.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../mlocati/ip-lib',
'aliases' => array(),
'reference' => 'c77bd0b1f3e3956c7e9661e75cb1f54ed67d95d2',
'dev_requirement' => false,
),
'paragonie/random_compat' => array(
'pretty_version' => 'v2.0.21',
'version' => '2.0.21.0',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/random_compat',
'aliases' => array(),
'reference' => '96c132c7f2f7bc3230723b66e89f8f150b29d5ae',
'dev_requirement' => false,
),
'privatebin/privatebin' => array(
'pretty_version' => 'dev-master',
'version' => 'dev-master',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
'reference' => '78aa70e3ab9277172489f9d88f8fd08cc1d03c97',
'dev_requirement' => false,
),
'yzalis/identicon' => array(
'pretty_version' => '2.0.0',
'version' => '2.0.0.0',
'type' => 'library',
'install_path' => __DIR__ . '/../yzalis/identicon',
'aliases' => array(),
'reference' => 'ff5ed090129cab9bfa2a322857d4a01d107aa0ae',
'dev_requirement' => false,
),
),
);

26
vendor/composer/platform_check.php vendored Normal file
View File

@@ -0,0 +1,26 @@
<?php
// platform_check.php @generated by Composer
$issues = array();
if (!(PHP_VERSION_ID >= 50600)) {
$issues[] = 'Your Composer dependencies require a PHP version ">= 5.6.0". You are running ' . PHP_VERSION . '.';
}
if ($issues) {
if (!headers_sent()) {
header('HTTP/1.1 500 Internal Server Error');
}
if (!ini_get('display_errors')) {
if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') {
fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL);
} elseif (!headers_sent()) {
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
}
}
trigger_error(
'Composer detected issues in your platform: ' . implode(' ', $issues),
E_USER_ERROR
);
}

View File

@@ -0,0 +1,130 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas;
use Jdenticon\Canvas\Rasterization\Edge;
use Jdenticon\Canvas\Rasterization\EdgeTable;
use Jdenticon\Canvas\Rasterization\Rasterizer;
use Jdenticon\Canvas\Png\PngPalette;
use Jdenticon\Canvas\Png\PngEncoder;
use Jdenticon\Canvas\CanvasContext;
use Jdenticon\Canvas\ColorUtils;
class Canvas
{
private $edges;
/**
* Creates a new canvas with the specified dimensions given in pixels.
*
* @param integer $width Canvas width in pixels.
* @param integer $height Canvas height in pixels.
*/
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
$this->edges = new EdgeTable($width, $height);
}
/**
* The width of the canvas in pixels.
*
* @var integer
*/
public $width = 0;
/**
* The height of the canvas in pixels.
*
* @var integer
*/
public $height = 0;
/**
* Specifies the background color. Allowed values are:
* - 32 bit integers on the format 0xRRGGBBAA
* - strings on the format #RGB
* - strings on the format #RRGGBB
* - strings on the format #RRGGBBAA
*
* @var integer|string
*/
public $backColor = 0x00000000;
/**
* Gets a context used to draw polygons on this canvas.
*
* @returns \Jdenticon\Canvas\CanvasContext
*/
public function getContext()
{
return new CanvasContext($this, $this->edges);
}
/**
* Renders the canvas as a PNG data stream.
*
* @param array $keywords Keywords to be written to the PNG stream.
* See https://www.w3.org/TR/PNG/#11keywords.
* @returns string
*/
public function toPng($keywords = array())
{
$colorRanges = array();
Rasterizer::rasterize(
$colorRanges, $this->edges,
$this->width, $this->height);
$backColor = ColorUtils::parse($this->backColor);
if (ColorUtils::alpha($backColor) > 0) {
$isColor = false;
foreach ($colorRanges as & $value) {
if ($isColor) {
$value = ColorUtils::over($value, $backColor);
$isColor = false;
} else {
$isColor = true;
}
}
unset($value);
}
$palette = new PngPalette($colorRanges);
$png = new PngEncoder();
$png->writeImageHeader($this->width, $this->height, $palette->isValid ?
PngEncoder::INDEXED_COLOR : PngEncoder::TRUE_COLOR_WITH_ALPHA);
$png->writeImageGamma();
foreach ($keywords as $key => $value) {
$png->writeTextualData($key, $value);
}
if ($palette && $palette->isValid) {
$png->writePalette($palette);
$png->writeTransparency($palette);
$png->writeIndexed($colorRanges, $palette,
$this->width, $this->height);
} else {
$png->writeTrueColorWithAlpha($colorRanges,
$this->width, $this->height);
}
$png->writeImageEnd();
return $png->getBuffer();
}
}

View File

@@ -0,0 +1,408 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas;
use Jdenticon\Canvas\ColorUtils;
use Jdenticon\Canvas\CanvasState;
use Jdenticon\Canvas\Rasterization\EdgeTable;
use Jdenticon\Canvas\Rasterization\Edge;
use Jdenticon\Canvas\Matrix;
class CanvasContext
{
private $savedStates = array();
private $edges;
private $transform;
private $paths;
private $canvas;
/**
* Creates a new canvas with the specified dimensions given in pixels.
*
* @param \Jdenticon\Canvas\Canvas $canvas The owner canvas.
* @param array $edges The owner canvas' edge buffer.
*/
public function __construct($canvas, &$edges)
{
$this->edges = $edges;
$this->canvas = $canvas;
$this->beginPath();
$this->resetTransform();
}
/**
* Specifies the fill color that is used when the fill method is called. Allowed values are:
* - 32 bit integers on the format 0xRRGGBBAA
* - strings on the format #RGB
* - strings on the format #RRGGBB
* - strings on the format #RRGGBBAA
*
* @var integer|string
*/
public $fillStyle = 0x000000ff;
/**
* Saves the current state to the state stack.
*/
public function save()
{
array_push($this->savedStates, array(
'transform' => $this->transform,
'fillStyle' => $this->fillStyle
));
}
/**
* Restores the last saved state of the CanvasContext.
*/
public function restore()
{
$state = array_pop($this->savedStates);
if ($state != NULL) {
$this->transform = $state['transform'];
$this->fillStyle = $state['fillStyle'];
}
}
/**
* Resets the internal path buffer and begins a new path.
*/
public function resetTransform()
{
$this->transform = new Matrix(1, 0, 0, 1, 0, 0);
}
/**
* Multiplies the current transformation matrix with the specified values.
*/
public function transform($a, $b, $c, $d, $e, $f)
{
if (gettype($a) != 'integer' ||
gettype($b) != 'integer' ||
gettype($c) != 'integer' ||
gettype($d) != 'integer' ||
gettype($e) != 'integer' ||
gettype($f) != 'integer'
) {
return;
}
$this->transform = $this->transform->multiply($a, $b, $c, $d, $e, $f);
}
/**
* Sets the transformation matrix to the specified matrix.
*/
public function setTransform($a, $b, $c, $d, $e, $f)
{
if (gettype($a) != 'integer' ||
gettype($b) != 'integer' ||
gettype($c) != 'integer' ||
gettype($d) != 'integer' ||
gettype($e) != 'integer' ||
gettype($f) != 'integer'
) {
return;
}
$this->transform = new Matrix($a, $b, $c, $d, $e, $f);
}
/**
* Applies a translation transformation to the CanvasContext.
*
* @param float $x Distance to move in the horizontal direction in pixels.
* @param float $y Distance to move in the vertical direction in pixels.
*/
public function translate($x, $y)
{
$this->transform = $this->transform->translate($x, $y);
}
/**
* Applies a scale transformation to the CanvasContext.
*
* @param float $x Scale in the horizontal direction. 1 means no scale.
* @param float $y Scale in the vertical direction. 1 means no scale.
*/
public function scale($x, $y)
{
$this->transform = $this->transform->scale($x, $y);
}
/**
* Applies a rotation transformation to the canvas around its current origo.
*
* @param float $angle Angle in radians measured clockwise from the
* positive x axis.
*/
public function rotate($angle)
{
$this->transform = $this->transform->rotate($angle);
}
/**
* Removes all existing subpaths and begins a new path.
*/
public function beginPath()
{
$this->paths = array();
}
/**
* Starts a new subpath that begins in the same point as the start and end
* point of the previous one.
*/
public function closePath()
{
$pathsCount = count($this->paths);
if ($pathsCount > 0) {
$path = $this->paths[$pathsCount - 1];
$pathCount = count($path);
if ($pathCount > 2) {
// Close path
if ($path[0] != $path[$pathCount - 2] ||
$path[1] != $path[$pathCount - 1]
) {
$path[] = $path[0];
$path[] = $path[1];
}
// Begin a new path
$this->paths[] = array($path[0], $path[1]);
}
}
}
/**
* Begins a new subpath by moving the cursor to the specified position.
*
* @param float $x X coordinate.
* @param float $y Y coordinate.
*/
public function moveTo($x, $y)
{
$p = $this->transform->multiplyPoint($x, $y);
$this->paths[] = array($p->x, $p->y);
}
/**
* Inserts an edge between the last and specified position.
*
* @param float $x Target X coordinate.
* @param float $y Target Y coordinate.
* @public
*/
public function lineTo($x, $y)
{
$pathsCount = count($this->paths);
if ($pathsCount == 0) {
$this->paths[] = array();
$pathsCount++;
}
$p = $this->transform->multiplyPoint($x, $y);
$path = &$this->paths[$pathsCount - 1];
$path[] = $p->x;
$path[] = $p->y;
}
/**
* Adds an arc to the current path.
*
* @param float $x X coordinate of the center of the arc.
* @param float $y Y coordinate of the center of the arc.
* @param float $radius Radius of the arc.
* @param float $startAngle The angle in radians at which the arc starts,
* measured clockwise from the positive x axis.
* @param float $endAngle The angle in radians at which the arc end,
* measured clockwise from the positive x axis.
* @param boolean $anticlockwise Specifies whether the arc will be drawn
* counter clockwise. Default is clockwise.
*/
public function arc($x, $y, $radius, $startAngle, $endAngle, $anticlockwise)
{
$TARGET_CHORD_LENGTH_PIXELS = 3;
$sectors = floor((M_PI * $radius * 2) / $TARGET_CHORD_LENGTH_PIXELS);
if ($sectors < 9) {
$sectors = 9;
}
$sectorAngle = M_PI * 2 / $sectors;
if ($startAngle == $endAngle) {
return;
}
if ($anticlockwise) {
$sectorAngle = -$sectorAngle;
if ($startAngle - $endAngle >= M_PI * 2) {
$endAngle = $startAngle - M_PI * 2;
} else {
// Normalize end angle so that the sweep angle is in the range
// (0, -2PI]
$endAngle +=
M_PI * 2 * ceil(($startAngle - $endAngle) /
(M_PI * 2) - 1);
}
} else {
if ($endAngle - $startAngle >= M_PI * 2) {
$endAngle = $startAngle + M_PI * 2;
} else {
// Normalize end angle so that the sweep angle is in the range
// (0, 2PI]
$endAngle -=
M_PI * 2 * ceil(($endAngle - $startAngle) /
(M_PI * 2) - 1);
}
}
$dx;
$dy;
$sectors = ($endAngle - $startAngle) / $sectorAngle;
$angle = $startAngle;
for ($i = 0; $i < $sectors; $i++) {
$dx = cos($angle) * $radius;
$dy = sin($angle) * $radius;
$this->lineTo($x + $dx, $y + $dy);
$angle += $sectorAngle;
}
$dx = cos($endAngle) * $radius;
$dy = sin($endAngle) * $radius;
$this->lineTo($x + $dx, $y + $dy);
}
/**
* Fills the specified rectangle with fully transparent black without
* affecting the current paths.
*
* @param float $x X coordinate of the left side of the rectangle.
* @param float $y Y coordinate of the top of the rectangle.
* @param float $width Width of the rectangle.
* @param float $height Height of the rectangle.
*/
public function clearRect($x, $y, $width, $height)
{
$fullCanvas = false;
if (!$this->transform->hasSkewing()) {
// Check if the whole canvas is cleared
$topLeft = $this->transform->multiplyPoint($x, $y);
if ($topLeft->x <= 0 && $topLeft->y <= 0) {
$bottomRight = $this->transform->multiplyPoint(
$x + $width, $y + $height);
if ($bottomRight->x >= $this->canvas->width &&
$bottomRight->y >= $this->canvas->height
) {
$fullCanvas = true;
}
}
}
if ($fullCanvas) {
$this->edges->clear();
} else {
$this->_fillRect(ColorUtils::FORCE_TRANSPARENT,
$x, $y, $width, $height);
}
}
/**
* Fills the specified rectangle without affecting the current paths.
*
* @param float $x X coordinate of the left side of the rectangle.
* @param float $y Y coordinate of the top of the rectangle.
* @param float $width Width of the rectangle.
* @param float $height Height of the rectangle.
*/
public function fillRect($x, $y, $width, $height)
{
$fillColor = ColorUtils::parse($this->fillStyle);
$this->_fillRect($fillColor, $x, $y, $width, $height);
}
private function _fillRect($fillColor, $x, $y, $width, $height)
{
$polygonId = $this->edges->getNextPolygonId();
$points = array(
$this->transform->multiplyPoint($x, $y),
$this->transform->multiplyPoint($x + $width, $y),
$this->transform->multiplyPoint($x + $width, $y + $height),
$this->transform->multiplyPoint($x, $y + $height),
$this->transform->multiplyPoint($x, $y)
);
$pointsCount = count($points);
for ($i = 1; $i < $pointsCount; $i++) {
$this->edges->add(new Edge(
$polygonId,
$points[$i - 1]->x,
$points[$i - 1]->y,
$points[$i]->x,
$points[$i]->y,
$fillColor));
}
}
/**
* Fills the defined paths.
*
* @param string $windingRule The winding rule to be used for determining
* which areas are covered by the current path. Valid values are
* "evenodd" and "nonzero". Default is "nonzero".
*/
public function fill($windingRule = "nonzero")
{
$polygonId = $this->edges->getNextPolygonId();
$fillColor = ColorUtils::parse($this->fillStyle);
foreach ($this->paths as $points) {
$pointsCount = count($points);
if ($pointsCount <= 2) {
// Nothing to fill
continue;
}
for ($i = 2; $i < $pointsCount; $i += 2) {
$this->edges->add(new Edge(
$polygonId,
$points[$i - 2],
$points[$i - 1],
$points[$i],
$points[$i + 1],
$fillColor,
$windingRule));
}
// Close path
if ($points[0] != $points[$pointsCount - 2] ||
$points[1] != $points[$pointsCount - 1]
) {
$this->edges->add(new Edge(
$polygonId,
$points[$pointsCount - 2],
$points[$pointsCount - 1],
$points[0],
$points[1],
$fillColor,
$windingRule));
}
}
}
}

View File

@@ -0,0 +1,228 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas;
class ColorUtils
{
/**
* Transparent color.
* @var integer
*/
const TRANSPARENT = 0;
/**
* Specifies a transparent color that will not blend with layers below the
* current layer.
*
* @var float
*/
const FORCE_TRANSPARENT = INF;
/**
* Creates a color on the format 0xRRGGBBAA from the specified
* color components.
*
* @return integer
*/
public static function from($a, $r, $g, $b)
{
return ($r << 24) | ($g << 16) | ($b << 8) | $a;
}
/**
* Gets the alpha component of a color.
*
* @param integer $color 32-bit color value on the format 0xRRGGBBAA.
* @return integer Alpha in the range [0, 255].
*/
public static function alpha($color)
{
return $color & 0xff;
}
/**
* Gets the red component of a color.
*
* @param integer $color 32-bit color value on the format 0xRRGGBBAA.
* @return integer Red component in the range [0, 255].
*/
public static function red($color)
{
return ($color >> 24) & 0xff;
}
/**
* Gets the green component of a color.
*
* @param integer $color 32-bit color value on the format 0xRRGGBBAA.
* @return integer Green component in the range [0, 255].
*/
public static function green($color)
{
return ($color >> 16) & 0xff;
}
/**
* Gets the blue component of a color.
*
* @param integer $color 32-bit color value on the format 0xRRGGBBAA.
* @return integer Blue component in the range [0, 255].
*/
public static function blue($color)
{
return ($color >> 8) & 0xff;
}
/**
* Formats a color as a string.
*
* @param integer $color Color to format.
* @return string
*/
public static function format($color)
{
return bin2hex(pack('N', $color));
}
/**
* Computes a mix of the two specified colors, with the proportion given
* by the specified weight.
*
* @param integer $color1 First color to mix.
* @param integer $color2 Second color to mix.
* @param float $weight Weight in the range [0,1].
* 0 gives $color1, 1 gives $color2.
* @return integer Mixed color.
*/
public static function mix($color1, $color2, $weight)
{
if ($weight < 0) {
$weight = 0;
} elseif ($weight > 1) {
$weight = 1;
}
$a = ($color1 & 0xff) * (1 - $weight) + ($color2 & 0xff) * $weight;
if ($a <= 0.1) {
return 0;
}
$r = (
($color1 >> 24) * ($color1 & 0xff) * (1 - $weight) +
($color2 >> 24) * ($color2 & 0xff) * $weight
) / $a;
$g = (
(($color1 >> 16) & 0xff) * ($color1 & 0xff) * (1 - $weight) +
(($color2 >> 16) & 0xff) * ($color2 & 0xff) * $weight
) / $a;
$b = (
(($color1 >> 8) & 0xff) * ($color1 & 0xff) * (1 - $weight) +
(($color2 >> 8) & 0xff) * ($color2 & 0xff) * $weight
) / $a;
if ($a > 255) $a = 255;
if ($r > 255) $r = 255;
if ($g > 255) $g = 255;
if ($b > 255) $b = 255;
return ((int)$r << 24) | ((int)$g << 16) | ((int)$b << 8) | (int)$a;
}
/**
* Parses a value to a 32-bit color on the format 0xRRGGBBAA.
*
* @param integer|string $color The value to parse.
* @return integer
*/
public static function parse($color)
{
if (gettype($color) == "integer") {
return $color & 0xffffffff;
}
$color = "$color";
if (preg_match('/^#?[0-9a-fA-F]+$/', $color)) {
$hexColor = $color;
if ($hexColor[0] == '#') {
$hexColor = substr($hexColor, 1);
}
switch (strlen($hexColor)) {
case 3:
$numeric = intval($hexColor, 16);
return (
(($numeric & 0xf00) << 20) |
(($numeric & 0xf00) << 16) |
(($numeric & 0x0f0) << 16) |
(($numeric & 0x0f0) << 12) |
(($numeric & 0x00f) << 12) |
(($numeric & 0x00f) << 8) |
0xff);
case 6:
return (intval($hexColor, 16) << 8) | 0xff;
case 8:
// Workaround to cope with PHP limitation of intval
$numeric =
(intval(substr($hexColor, 0, 4), 16) << 16) |
(intval(substr($hexColor, 4, 4), 16));
return $numeric;
}
}
throw new \InvalidArgumentException("Invalid color '$color'.");
}
/**
* Blends this color with another color using the over blending operation.
*
* @param integer $fore The foreground color.
* @param integer $back The background color.
* @return integer
*/
public static function over($fore, $back)
{
$foreA = ($fore & 0xff);
$backA = ($back & 0xff);
if ($foreA < 1) {
return $back;
} elseif ($foreA > 254 || $backA < 1) {
return $fore;
}
// Source:
// https://en.wikipedia.org/wiki/Alpha_compositing#Description
$forePA = $foreA * 255;
$backPA = $backA * (255 - $foreA);
$pa = ($forePA + $backPA);
$b = (int) (
($forePA * (($fore >> 8) & 0xff) + $backPA * (($back >> 8) & 0xff)) /
$pa);
$g = (int) (
($forePA * (($fore >> 16) & 0xff) + $backPA * (($back >> 16) & 0xff)) /
$pa);
$r = (int) (
($forePA * (($fore >> 24) & 0xff) + $backPA * (($back >> 24) & 0xff)) /
$pa);
$a = (int) ($pa / 255);
return ($r << 24) | ($g << 16) | ($b << 8) | $a;
}
};

View File

@@ -0,0 +1,141 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas;
use Jdenticon\Canvas\Point;
class Matrix
{
private $a;
private $b;
private $c;
private $d;
private $e;
private $f;
/**
* Creates a new transformation matrix.
*/
public function __construct($a, $b, $c, $d, $e, $f)
{
$this->a = $a;
$this->b = $b;
$this->c = $c;
$this->d = $d;
$this->e = $e;
$this->f = $f;
}
/**
* Gets a value determining if this matrix has skewing values.
*
* @return boolean
*/
public function hasSkewing()
{
return $this->b || $this->c;
}
/**
* Gets a value determining if this matrix has translation values.
*
* @return boolean
*/
public function hasTranslation()
{
return $this->e || $this->f;
}
/**
* Gets a value determining if this matrix has scaling values.
*
* @return boolean
*/
public function hasScaling()
{
return $this->a != 1 || $this->d != 1;
}
/**
* Returns a new matrix based on the current matrix multiplied with the
* specified matrix values.
*
* @return \Jdenticon\Canvas\Matrix
*/
public function multiply($a, $b, $c, $d, $e, $f)
{
return new Matrix(
$this->a * $a + $this->c * $b,
$this->b * $a + $this->d * $b,
$this->a * $c + $this->c * $d,
$this->b * $c + $this->d * $d,
$this->a * $e + $this->c * $f + $this->e,
$this->b * $e + $this->d * $f + $this->f
);
}
/**
* Multiplies the specified point with the current matrix and returns the
* resulting point.
*
* @param float $x X coordinate.
* @param float $y Y coordinate.
* @return \Jdenticon\Canvas\Point
*/
public function multiplyPoint($x, $y)
{
return new Point(
$this->a * $x + $this->c * $y + $this->e,
$this->b * $x + $this->d * $y + $this->f
);
}
/**
* Returns a new matrix based on the current matrix with a rotation
* transformation applied.
*
* @param float $angle Rotation angle in radians.
* @return \Jdenticon\Canvas\Matrix
*/
public function rotate($angle)
{
$sin = sin($angle);
$cos = cos($angle);
return $this->multiply($cos, $sin, -$sin, $cos, 0, 0);
}
/**
* Returns a new matrix based on the current matrix with a translation
* transformation applied.
*
* @param float $x Horizontal move distance.
* @param float $y Vertical move distance.
* @return \Jdenticon\Canvas\Matrix
*/
public function translate($x, $y)
{
return $this->multiply(1, 0, 0, 1, $x, $y);
}
/**
* Returns a new matrix based on the current matrix with a scaling
* transformation applied.
*
* @param float $x Horizontal scale.
* @param float $y Vertical scale.
* @return \Jdenticon\Canvas\Matrix
*/
public function scale($x, $y)
{
return $this->multiply($x, 0, 0, $y, 0, 0);
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Png;
class PngBuffer
{
private $buffer = '';
private $chunkPreviousBuffer = '';
/**
* Writes a string to the buffer.
*
* @param string $str String to write.
*/
public function writeString($str)
{
$this->buffer .= $str;
}
/**
* Writes a 32 bit unsigned int to the buffer in Big Endian format.
*
* @param integer $value Value to write.
*/
public function writeUInt32BE($value)
{
$this->buffer .= pack('N', $value);
}
/**
* Writes an 8 bit unsigned int to the buffer.
*
* @param integer $value Value to write.
*/
public function writeUInt8($value)
{
$this->buffer .= pack('C', $value);
}
/**
* Starts a new PNG chunk.
*
* @param string $type Name of the chunk. Must contain exactly 4
* ASCII characters.
*/
public function startChunk($type)
{
$this->chunkPreviousBuffer = $this->buffer;
$this->buffer = $type;
}
/**
* Closes the current PNG chunk.
*/
public function endChunk()
{
// Compute Crc32 for type + data
$data = $this->buffer;
$crc = crc32($data);
$this->buffer =
$this->chunkPreviousBuffer .
// Length
pack('N', strlen($data) - 4) .
// Content
$data .
// Crc32
pack('N', $crc);
}
/**
* Gets a string containing the PNG encoded data.
*
* @return string
*/
public function getBuffer()
{
return $this->buffer;
}
}

View File

@@ -0,0 +1,238 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Png;
use Jdenticon\Canvas\Png\PngPalette;
use Jdenticon\Canvas\Png\PngBuffer;
use Jdenticon\Canvas\ColorUtils;
class PngEncoder
{
const GRAYSCALE = 0;
const TRUE_COLOR = 2;
const INDEXED_COLOR = 3;
const GRAYSCALE_WITH_ALPHA = 4;
const TRUE_COLOR_WITH_ALPHA = 6;
private $buffer;
public function __construct()
{
$this->buffer = new PngBuffer();
$this->buffer->writeString("\x89\x50\x4e\x47\xd\xa\x1a\xa");
}
/**
* Writes an IHDR chunk to the png data stream.
*
* @param int $width Image width in pixels.
* @param int $height Image height in pixels.
* @param int $colorType Color depth, speocfy one of the constants in
* PngEncoder.
*/
public function writeImageHeader($width, $height, $colorType)
{
$this->buffer->startChunk("IHDR");
$this->buffer->writeUInt32BE($width);
$this->buffer->writeUInt32BE($height);
$this->buffer->writeUInt8(8); // Bit depth
$this->buffer->writeUInt8($colorType);
$this->buffer->writeUInt8(0); // Compression
$this->buffer->writeUInt8(0); // Filter
$this->buffer->writeUInt8(0); // Interlace
$this->buffer->endChunk();
}
/**
* Writes a gAMA chunk to the png data stream.
*
* @param int $gamma Gamma value.
*/
public function writeImageGamma($gamma = 45455)
{
$this->buffer->startChunk("gAMA");
$this->buffer->writeUInt32BE($gamma);
$this->buffer->endChunk();
}
/**
* Writes an IDAT chunk of truecolor encoded image data.
*
* @param array $colorRanges Image data on the format
* array(count0, color0, count1, color1, ...)
* @param int $width Image width in pixels.
* @param int $height Image height in pixels.
*/
public function writeTrueColorWithAlpha(
array & $colorRanges, $width, $height)
{
$this->buffer->startChunk("IDAT");
$uncompressed = '';
$count = -1;
$x = 0;
foreach ($colorRanges as $value) {
if ($count === -1) {
$count = $value;
} else {
if ($count !== 0) {
if ($x === $width) {
$x = 0;
}
if ($x === 0) {
$uncompressed .= pack('C', 0); // No filtering
}
$uncompressed .= str_repeat(pack('N', $value), $count);
$x += $count;
}
$count = -1;
}
}
$compressed = gzcompress($uncompressed, 2);
$this->buffer->writeString($compressed);
$this->buffer->endChunk();
}
/**
* Writes an IDAT chunk of indexed image data.
*
* @param array $colorRanges Image data on the format
* array(count0, color0, count1, color1, ...)
* @param \Jdenticon\Canvas\Png\PngPalette $palette Palette containing the
* indexed colors.
* @param int $width Image width in pixels.
* @param int $height Image height in pixels.
*/
public function writeIndexed(
array & $colorRanges,
PngPalette $palette,
$width, $height)
{
$this->buffer->startChunk("IDAT");
$uncompressed = '';
$count = -1;
$x = 0;
foreach ($colorRanges as $value) {
if ($count === -1) {
$count = $value;
} else {
if ($count !== 0) {
if ($x === $width) {
$x = 0;
}
if ($x === 0) {
$uncompressed .= pack('C', 0); // No filtering
}
$colorIndex = $palette->lookup[$value];
$uncompressed .= str_repeat(pack('C', $colorIndex), $count);
$x += $count;
}
$count = -1;
}
}
$compressed = gzcompress($uncompressed, 2);
$this->buffer->writeString($compressed);
$this->buffer->endChunk();
}
/**
* Writes a PLTE chunk containing the indexed colors.
*
* @param \Jdenticon\Canvas\Png\PngPalette $palette Palette containing the
* indexed colors.
*/
public function writePalette(PngPalette $palette)
{
if ($palette && $palette->isValid) {
$this->buffer->startChunk("PLTE");
foreach ($palette->colors as $color) {
$this->buffer->writeString(
pack('C', ($color >> 24) & 0xff) .
pack('C', ($color >> 16) & 0xff) .
pack('C', ($color >> 8) & 0xff));
}
$this->buffer->endChunk();
}
}
/**
* Writes a tRNS chunk containing the alpha values of indexed colors.
*
* @param \Jdenticon\Canvas\Png\PngPalette $palette Palette containing the
* indexed colors.
*/
public function writeTransparency(PngPalette $palette)
{
if ($palette && $palette->isValid && $palette->hasAlphaChannel) {
$this->buffer->startChunk("tRNS");
$alpha = '';
foreach ($palette->colors as $color) {
$alpha .= pack('C', $color & 0xff);
}
$this->buffer->writeString($alpha);
$this->buffer->endChunk();
}
}
/**
* Writes a tEXt chunk containing the specified strings.
*
* @param string $key Key, one of
* {@link https://www.w3.org/TR/2003/REC-PNG-20031110/#11keywords}
* @param string $value Value.
*/
public function writeTextualData($key, $value)
{
$this->buffer->startChunk("tEXt");
$this->buffer->writeString($key);
$this->buffer->writeUInt8(0);
$this->buffer->writeString($value);
$this->buffer->endChunk();
}
/**
* Writes an IEND chunk to the png data stream.
*/
public function writeImageEnd()
{
$this->buffer->startChunk("IEND");
$this->buffer->endChunk();
}
/**
* Gets a binary string containing the PNG data.
*
* @return string
*/
public function getBuffer()
{
return $this->buffer->getBuffer();
}
}

View File

@@ -0,0 +1,92 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Png;
use Jdenticon\Canvas\ColorUtils;
/**
* Contains the colors of a PNG color palette.
*/
class PngPalette
{
/**
* Creates a PNG color palette for the specified bitmap data.
*
* @param array(integer) $colorRanges Array of interleaved values on the
* format array(count0, color0, count1, color1, ...).
*/
function __construct(& $colorRanges)
{
$lookup = array();
$colors = array();
$hasAlphaChannel = false;
$colorsCount = 0;
$count = -1;
foreach ($colorRanges as $value) {
if ($count === -1) {
$count = $value;
} else {
// Ignore empty ranges and already indexed colors
if ($count > 0 && !isset($lookup[$value])) {
if (!$hasAlphaChannel && ($value & 0xff) < 255) {
$hasAlphaChannel = true;
}
$lookup[$value] = $colorsCount++;
$colors[] = $value;
if ($colorsCount > 256) {
break;
}
}
$count = -1;
}
}
$this->hasAlphaChannel = $hasAlphaChannel;
$this->colors = & $colors;
$this->lookup = & $lookup;
$this->isValid = $colorsCount <= 256;
}
/**
* Specifies if the palette is valid to be used for encoding a PNG image.
*
* @var boolean
*/
public $isValid;
/**
* Specifies if the palette has any partial or fully transparent
* colors.
*
* @var boolean
*/
public $hasAlphaChannel;
/**
* Array of colors in the palette.
*
* @var array
*/
public $colors;
/**
* Lookup table from 32-bit color value to color index.
*
* @var array
*/
public $lookup;
}

View File

@@ -0,0 +1,41 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas;
class Point
{
/**
* X coordinate.
*
* @var float
*/
public $x;
/**
* Y coordinate.
*
* @var float
*/
public $y;
/**
* Creates a new 2D point.
*
* @param float $x X coordinate.
* @param float $y Y coordinate.
*/
public function __construct($x, $y)
{
$this->x = $x;
$this->y = $y;
}
}

View File

@@ -0,0 +1,44 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
class Edge
{
public $polygonId;
public $x0;
public $x1;
public $y0;
public $y1;
public $color;
public $windingRule;
public function __construct(
$polygonId, $x0, $y0, $x1, $y1, $color, $windingRule = null)
{
$this->polygonId = $polygonId;
$this->x0 = $x0;
$this->x1 = $x1;
$this->y0 = $y0;
$this->y1 = $y1;
$this->color = $color;
$this->windingRule = $windingRule;
}
public function intersection($y)
{
$dx =
($this->x1 - $this->x0) * ($this->y0 - $y) /
($this->y0 - $this->y1);
return $this->x0 + $dx;
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
class EdgeIntersection
{
public $fromX;
public $width;
public $edge;
public function __construct($fromX, $width, $edge)
{
$this->fromX = $fromX;
$this->width = $width;
$this->edge = $edge;
}
}

View File

@@ -0,0 +1,24 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
class EdgeSuperSampleIntersection
{
public $x;
public $edge;
public function __construct($x, $edge)
{
$this->x = $x;
$this->edge = $edge;
}
}

View File

@@ -0,0 +1,158 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
class EdgeTable
{
private $scanlines;
private $nextPolygonId;
private $width;
private $height;
/**
* Keeps a list of edges per scanline.
*
* @param integer $width Clipping width.
* @param integer $height Clipping height.
*/
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
$this->clear();
}
/**
* Sorts the edges of each scanline in ascending x coordinates.
*/
public function clear()
{
$this->scanlines = array();
$this->nextPolygonId = 1;
}
/**
* Gets an id for the next polygon.
*
* @return int
*/
public function getNextPolygonId()
{
return $this->nextPolygonId++;
}
/**
* Gets the scaline for the specified Y coordinate, or NULL if there are
* no edges for the specified Y coordinate.
*
* @return array|null.
*/
public function getScanline($y)
{
return isset($this->scanlines[$y]) ? $this->scanlines[$y] : null;
}
/**
* Adds an edge to the table.
*
* @param \Jdenticon\Canvas\Rasterization\Edge $edge
*/
public function add(\Jdenticon\Canvas\Rasterization\Edge $edge)
{
$minY = 0;
$maxY = 0;
if ($edge->y0 == $edge->y1) {
// Skip horizontal lines
return;
} elseif ($edge->y0 < $edge->y1) {
$minY = (int)($edge->y0);
$maxY = (int)($edge->y1 + 0.996 /* 1/255 */);
} else {
$minY = (int)($edge->y1);
$maxY = (int)($edge->y0 + 0.996 /* 1/255 */);
}
if ($maxY < 0 || $minY >= $this->height) {
return;
}
if ($minY < 0) {
$minY = 0;
}
if ($maxY > $this->height) {
$maxY = $this->height;
}
if ($minY < $maxY) {
$y = $minY;
$x1 = $edge->intersection($y);
while ($y < $maxY) {
$x2 = $edge->intersection($y + 1);
$fromX;
$width;
if ($x1 < $x2) {
$fromX = (int)($x1);
$width = (int)($x2 + 0.9999) - $fromX;
} else {
$fromX = (int)($x2);
$width = (int)($x1 + 0.9999) - $fromX;
}
if ($fromX < 0) {
$width += $fromX;
$fromX = 0;
if ($width < 0) {
$width = 0;
}
}
if ($fromX < $this->width) {
if (!isset($this->scanlines[$y])) {
$this->scanlines[$y] = array();
}
$this->scanlines[$y][] = new EdgeIntersection(
$fromX, $width, $edge);
}
$x1 = $x2;
$y++;
}
}
}
private static function edge_cmp($x, $y)
{
if ($x->fromX < $y->fromX) {
return -1;
}
if ($x->fromX > $y->fromX) {
return 1;
}
return 0;
}
/**
* Sorts the edges of each scanline in ascending x coordinates.
*/
public function sort()
{
foreach ($this->scanlines as $i => &$scanline) {
usort($scanline, array(
'Jdenticon\\Canvas\\Rasterization\\EdgeTable', 'edge_cmp'));
}
}
}

View File

@@ -0,0 +1,30 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
class Layer
{
public $polygonId;
public $color;
public $winding;
public $windingRule;
public $nextLayer;
public function __construct($polygonId, $color, $winding, $windingRule)
{
$this->polygonId = $polygonId;
$this->color = $color;
$this->winding = $winding;
$this->windingRule = $windingRule;
}
}

View File

@@ -0,0 +1,150 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
use Jdenticon\Canvas\ColorUtils;
use Jdenticon\Canvas\Rasterization\Layer;
use Jdenticon\Canvas\Rasterization\Edge;
/**
* Keeps track of the z-order of the currently rendered polygons,
* and computes the final color from the stack of layers.
*/
class LayerManager
{
public $topLayer;
/**
* The current visible color.
* @var integer
*/
public $color;
public function __construct()
{
$this->color = ColorUtils::TRANSPARENT;
}
/**
* Copies all layers in this manager to another LayerManager.
*
* @param \Jdenticon\Canvas\Rasterization\LayerManager $other The
* LayerManager to copy all layers to.
*/
public function copyTo(LayerManager $other)
{
$other->color = $this->color;
$layer = $this->topLayer;
$previousCopy = null;
while ($layer !== null) {
$copy = new Layer(
$layer->polygonId,
$layer->color,
$layer->winding,
$layer->windingRule
);
if ($previousCopy === null) {
$other->topLayer = $copy;
}
else {
$previousCopy->nextLayer = $copy;
}
$previousCopy = $copy;
$layer = $layer->nextLayer;
}
}
/**
* Adds a layer for the specified edge. The z-order is defined by its id.
*
* @param \Jdenticon\Canvas\Rasterization\Edge edge
*/
public function add(Edge $edge)
{
$dwinding = $edge->y0 < $edge->y1 ? 1 : -1;
$layer = $this->topLayer;
$previousLayer = null;
while ($layer !== null) {
if ($layer->polygonId === $edge->polygonId) {
$layer->winding += $dwinding;
$inPath = $layer->windingRule == 'evenodd' ?
($layer->winding % 2 === 1) : ($layer->winding !== 0);
if (!$inPath) {
// Remove layer
if ($previousLayer === null) {
$this->topLayer = $layer->nextLayer;
}
else {
$previousLayer->nextLayer = $layer->nextLayer;
}
}
break;
} elseif ($layer->polygonId < $edge->polygonId) {
// Insert here
$newLayer = new Layer(
$edge->polygonId,
$edge->color,
$dwinding,
$edge->windingRule
);
$newLayer->nextLayer = $layer;
if ($previousLayer === null) {
$this->topLayer = $newLayer;
} else {
$previousLayer->nextLayer = $newLayer;
}
break;
}
$previousLayer = $layer;
$layer = $layer->nextLayer;
}
if ($layer === null) {
$newLayer = new Layer(
$edge->polygonId,
$edge->color,
$dwinding,
$edge->windingRule
);
if ($previousLayer === null) {
$this->topLayer = $newLayer;
} else {
$previousLayer->nextLayer = $newLayer;
}
}
// Update current color
$color = ColorUtils::TRANSPARENT;
$layer = $this->topLayer;
while ($layer !== null && ($color & 0xff) < 255) {
if ($layer->color === ColorUtils::FORCE_TRANSPARENT) {
break;
}
$color = ColorUtils::over($layer->color, $color);
}
$this->color = $color;
}
}

View File

@@ -0,0 +1,379 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
use Jdenticon\Canvas\ColorUtils;
use Jdenticon\Canvas\Rasterization\LayerManager;
use Jdenticon\Canvas\Rasterization\SuperSampleBuffer;
use Jdenticon\Canvas\Rasterization\SuperSampleRange;
use Jdenticon\Canvas\Rasterization\EdgeSuperSampleIntersection;
class Rasterizer
{
/**
* A higher number of samples per pixel horizontally does not affect the
* performance in the same way as SAMPLES_PER_PIXEL_Y, since the rasterizer
* does not scan every subpixel horizontally.
*/
const SAMPLES_PER_PIXEL_X = 10;
/**
* A higher number of samples vertically means lower performance, since
* the rasterizer does a scan for every subpixel vertically.
*/
const SAMPLES_PER_PIXEL_Y = 3;
const SAMPLE_HEIGHT = 0.33333; // 1 / self::SAMPLES_PER_PIXEL_Y
/**
* Rasterizes the edges in the edge table to a list of color ranges. No
* range will span multiple scanlines.
*/
public static function rasterize(& $colorData, $edgeTable, $width, $height)
{
$edgeTable->sort();
$superSampleBuffer = new SuperSampleBuffer(
$width, self::SAMPLES_PER_PIXEL_X);
$layers = array();
$color = 0;
// Keeps track of how many of the subpixellayers that are used for
// the currently rendered scanline. Until a range requiring
// supersampling is encountered only a single layer is needed.
$usedLayers = 0;
for ($i = 0; $i < self::SAMPLES_PER_PIXEL_Y; $i++) {
$layers[] = new LayerManager();
}
for ($ey = 0; $ey < $height; $ey++) {
$scanline = $edgeTable->getScanline($ey);
if ($scanline === null) {
$colorData[] = $width;
$colorData[] = 0;
continue;
}
$superSampleRanges = self::getSuperSampleRanges($scanline, $width);
$superSampleRangeCount = count($superSampleRanges);
foreach ($layers as $layer) {
$layer->topLayer = null;
$layer->color = ColorUtils::TRANSPARENT;
}
$usedLayers = 1;
if ($superSampleRanges[0]->fromX) {
$colorData[] = $superSampleRanges[0]->fromX;
$colorData[] = 0;
}
for (
$rangeIndex = 0;
$rangeIndex < $superSampleRangeCount;
$rangeIndex++
) {
$superSampleRange = $superSampleRanges[$rangeIndex];
$edge = $superSampleRange->edges[0];
// If there is exactly one edge in the supersample range, and it
// is crossing the entire scanline, we can perform the
// antialiasing by integrating the edge function.
if (!isset($superSampleRange->edges[1]) && (
$edge->y0 <= $ey && $edge->y1 >= $ey + 1 ||
$edge->y0 >= $ey + 1 && $edge->y1 <= $ey
)) {
// Determine the lower and upper x value where the edge
// intersects the scanline.
$xey = $edge->intersection($ey);
$xey1 = $edge->intersection($ey + 1);
if ($xey < $xey1) {
$x0 = $xey;
$x1 = $xey1;
} else {
$x0 = $xey1;
$x1 = $xey;
}
$rangeWidth = $x1 - $x0;
if ($usedLayers === 1) {
$subScanlineLayers = $layers[0];
$fromColor = $subScanlineLayers->color;
$subScanlineLayers->add($edge);
$toColor = $subScanlineLayers->color;
} else {
$fromColorR = 0;
$fromColorG = 0;
$fromColorB = 0;
$fromColorA = 0;
$toColorR = 0;
$toColorG = 0;
$toColorB = 0;
$toColorA = 0;
// Compute the average color of all subpixel layers
// before and after the edge intersection.
// The calculation is inlined for increased performance.
for ($i = 0; $i < $usedLayers; $i++) {
$subScanlineLayers = $layers[$i];
// Add to average from-color
$color = $subScanlineLayers->color;
$alpha = $color & 0xff;
if ($alpha > 0) {
$fromColorA += $alpha;
$fromColorR += (($color >> 24) & 0xff) * $alpha;
$fromColorG += (($color >> 16) & 0xff) * $alpha;
$fromColorB += (($color >> 8) & 0xff) * $alpha;
}
// Add the new layer
$subScanlineLayers->add($edge);
// Add to average to-color
$color = $subScanlineLayers->color;
$alpha = $color & 0xff;
if ($alpha > 0) {
$toColorA += $alpha;
$toColorR += (($color >> 24) & 0xff) * $alpha;
$toColorG += (($color >> 16) & 0xff) * $alpha;
$toColorB += (($color >> 8) & 0xff) * $alpha;
}
}
$fromColor = $fromColorA === 0 ? 0 : ColorUtils::from(
(int)($fromColorA / $usedLayers),
(int)($fromColorR / $fromColorA),
(int)($fromColorG / $fromColorA),
(int)($fromColorB / $fromColorA));
$toColor = $toColorA === 0 ? 0 : ColorUtils::from(
(int)($toColorA / $usedLayers),
(int)($toColorR / $toColorA),
(int)($toColorG / $toColorA),
(int)($toColorB / $toColorA));
}
// Render pixels
for (
$x = $superSampleRange->fromX;
$x < $superSampleRange->toXExcl;
$x++
) {
if ($x0 >= $x + 1) {
// Pixel not covered
$colorData[] = 1;
$colorData[] = $fromColor;
continue;
}
if ($x1 <= $x) {
// Pixel fully covered
$colorData[] = 1;
$colorData[] = $toColor;
continue;
}
// toColor coverage in the range [0.0, 1.0]
// Initialize to the fully covered range of the pixel.
$coverage = $x1 < $x + 1 ? $x + 1 - $x1 : 0;
// Compute integral for non-vertical edges
if ($rangeWidth > 0.001) {
// Range to integrate
$integralFrom = $x0 > $x ? $x0 : $x;;
$integralTo = $x1 < $x + 1 ? $x1 : $x + 1;
$coverage +=
(
(
$integralTo * $integralTo -
$integralFrom * $integralFrom
) / 2 +
$x0 * ($integralFrom - $integralTo)
) / $rangeWidth;
}
$colorData[] = 1;
$colorData[] = ColorUtils::mix(
$fromColor, $toColor, $coverage);
}
$color = $toColor;
} // /simplified antialiasing
else {
// Super sampling
$y = $ey + self::SAMPLE_HEIGHT / 2;
// Ensure all subpixel layers are initialized
while ($usedLayers < self::SAMPLES_PER_PIXEL_Y) {
$layers[0]->copyTo($layers[$usedLayers]);
$usedLayers++;
}
foreach ($layers as $subScanlineLayers) {
$color = $subScanlineLayers->color;
$intersections = self::getIntersections(
$superSampleRange->edges, $y);
foreach ($intersections as $intersection) {
$superSampleBuffer->add($color,
$intersection->x - $superSampleRange->fromX);
$subScanlineLayers->add($intersection->edge);
$color = $subScanlineLayers->color;
}
$superSampleBuffer->add(
$color, $superSampleRange->width + 1);
$superSampleBuffer->rewind();
$y += self::SAMPLE_HEIGHT;
} // /subpixel
// Blend subpixels
$color = $superSampleBuffer->colorAt(
$superSampleRange->width);
$superSampleBuffer->emptyTo(
$colorData, $superSampleRange->width);
//$color = end($colorData);
} // /super sampling
// Forward last color
if ($rangeIndex + 1 < $superSampleRangeCount) {
$count =
$superSampleRanges[$rangeIndex + 1]->fromX -
$superSampleRange->toXExcl;
if ($count > 0) {
$colorData[] = $count;
$colorData[] = $color;
}
} else {
$count = $width - $superSampleRange->toXExcl;
if ($count > 0) {
$colorData[] = $count;
$colorData[] = $color;
}
}
} // /range
}
return $colorData;
}
private static function intersection_cmp($a, $b)
{
if ($a->x < $b->x) {
return -1;
}
if ($a->x > $b->x) {
return 1;
}
return 0;
}
/**
* Determines what edges that intersect a horizontal line with the specified
* y coordinate. For each intersecting edge the intersecting x coordinate is
* returned.
*
* @param array $edges Array of edges in the current scanline.
* @param int $y Y coordinate of the current scanline.
* @return array Array containing EdgeSuperSampleIntersection. Objects
* are sorted ascending by x coordinate.
*/
private static function getIntersections($edges, $y)
{
$intersections = array();
foreach ($edges as $edge) {
if ($edge->y0 < $y && $edge->y1 >= $y ||
$edge->y0 >= $y && $edge->y1 < $y
) {
$x = $edge->x0 +
($edge->x1 - $edge->x0) * ($y - $edge->y0) /
($edge->y1 - $edge->y0);
$intersections[] = new EdgeSuperSampleIntersection($x, $edge);
}
}
usort($intersections, array(
'Jdenticon\\Canvas\\Rasterization\\Rasterizer',
'intersection_cmp'));
return $intersections;
}
/**
* Determines what ranges of a scanline that needs to be supersampled.
*
* @param array $scanline Array of edges in the current scanline.
* @return array Array of SuperSampleRange.
*/
private static function getSuperSampleRanges(&$scanline, $width)
{
$superSampleRanges = array();
$rangeIndex = 0;
$scanlineCount = count($scanline);
while ($rangeIndex < $scanlineCount) {
$range = $scanline[$rangeIndex];
if ($range->fromX >= $width) {
break;
}
$superSampleRange = new SuperSampleRange(
$range->fromX,
$range->fromX + $range->width
);
$superSampleRange->edges[] = $range->edge;
$rangeIndex++;
for ($i = $rangeIndex; $i < $scanlineCount; $i++) {
$range = $scanline[$i];
if ($range->fromX < $superSampleRange->toXExcl) {
$superSampleRange->toXExcl = max(
$superSampleRange->toXExcl,
$range->fromX + $range->width);
$superSampleRange->edges[] = $range->edge;
$rangeIndex++;
} else {
break;
}
}
$superSampleRange->toXExcl = min($superSampleRange->toXExcl, $width);
$superSampleRange->width =
$superSampleRange->toXExcl - $superSampleRange->fromX;
$superSampleRanges[] = $superSampleRange;
}
return $superSampleRanges;
}
}

View File

@@ -0,0 +1,198 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
use Jdenticon\Canvas\ColorUtils;
class SuperSampleBuffer
{
const IDX_COUNT = 0;
const IDX_A = 1;
const IDX_R = 2;
const IDX_G = 3;
const IDX_B = 4;
/**
* Creates a color buffer keeping an average color out of several
* color samples per pixel.
*
* @param integer $width Width of the buffer in pixels.
* @param integer $samplesPerPixel Number of samples to keep per pixel.
*/
public function __construct($width, $samplesPerPixel)
{
$this->samples = array();
$this->samplesPerPixel = $samplesPerPixel;
$this->pixelOffset = 0;
$this->subPixelOffset = 0;
$this->width = $width;
$this->used = -1;
}
/**
* Rewinds the cursor to the beginning of the buffer.
*/
public function rewind()
{
$this->pixelOffset = 0;
$this->subPixelOffset = 0;
}
/**
* Clears the samples in this buffer.
*/
public function clear()
{
$this->pixelOffset = 0;
$this->subPixelOffset = 0;
$this->used = -1;
}
/**
* Writes the average color of each pixel to a specified color array.
*
* @param array $colorData The average colors will be written to this
* color array.
* @param integer $count Number of pixels to write.
*/
public function emptyTo(& $colorData, $count)
{
for ($i = 0; $i < $count; $i++) {
$sampleCount = $this->samples[$i * 5 + self::IDX_COUNT];
$a = $this->samples[$i * 5 + self::IDX_A];
$color = $sampleCount == 0 || $a == 0 ? 0 :
ColorUtils::from(
(int)($a / $sampleCount),
(int)($this->samples[$i * 5 + self::IDX_R] / $a),
(int)($this->samples[$i * 5 + self::IDX_G] / $a),
(int)($this->samples[$i * 5 + self::IDX_B] / $a)
);
$colorData[] = 1;
$colorData[] = $color;
}
$this->pixelOffset = 0;
$this->subPixelOffset = 0;
$this->used = -1;
}
/**
* Gets the average color of the pixel at a specified index.
*
* @param integer $index The index of the pixel.
* @return integer
*/
public function colorAt($index)
{
$sampleCount = $this->samples[$index * 5 + self::IDX_COUNT];
$alphaSum = $this->samples[$index * 5 + self::IDX_A];
return $sampleCount == 0 || $alphaSum == 0 ? 0 :
ColorUtils::from(
(int)($alphaSum / $sampleCount),
(int)($this->samples[$index * 5 + self::IDX_R] / $alphaSum),
(int)($this->samples[$index * 5 + self::IDX_G] / $alphaSum),
(int)($this->samples[$index * 5 + self::IDX_B] / $alphaSum)
);
}
/**
* Adds a color to the current pixel in the buffer.
*
* @param integer $count Number of samples of the color to be added to
* the buffer.
*/
private function _add($count, $a, $r, $g, $b)
{
if ($this->used < $this->pixelOffset) {
$this->used = $this->pixelOffset;
$this->samples[$this->pixelOffset * 5 + self::IDX_COUNT] = $count;
$this->samples[$this->pixelOffset * 5 + self::IDX_A] = $a * $count;
$this->samples[$this->pixelOffset * 5 + self::IDX_R] = $a * $r * $count;
$this->samples[$this->pixelOffset * 5 + self::IDX_G] = $a * $g * $count;
$this->samples[$this->pixelOffset * 5 + self::IDX_B] = $a * $b * $count;
} else {
$this->samples[$this->pixelOffset * 5 + self::IDX_COUNT] += $count;
if ($a > 0) {
$this->samples[$this->pixelOffset * 5 + self::IDX_A] += $a * $count;
$this->samples[$this->pixelOffset * 5 + self::IDX_R] += $a * $r * $count;
$this->samples[$this->pixelOffset * 5 + self::IDX_G] += $a * $g * $count;
$this->samples[$this->pixelOffset * 5 + self::IDX_B] += $a * $b * $count;
}
}
}
/**
* Adds a color to the buffer up until the specified x index.
*
* @param integer $color Color to write.
* @param float $untilX Samples of the color will be added the buffer until
* the cursor reaches this coordinate.
*/
public function add($color, $untilX)
{
$samplesLeft =
(int)($untilX * $this->samplesPerPixel) -
$this->subPixelOffset -
$this->pixelOffset * $this->samplesPerPixel;
// ColorUtils methods inlined for performance reasons
$a = ($color) & 0xff;
$r = ($color >> 24) & 0xff;
$g = ($color >> 16) & 0xff;
$b = ($color >> 8) & 0xff;
// First partial pixel
if ($this->subPixelOffset > 0) {
$samples = $this->samplesPerPixel - $this->subPixelOffset;
if ($samples > $samplesLeft) {
$samples = $samplesLeft;
}
$samplesLeft -= $samples;
$this->_add($samples, $a, $r, $g, $b);
$this->subPixelOffset += $samples;
if ($this->subPixelOffset == $this->samplesPerPixel) {
$this->subPixelOffset = 0;
$this->pixelOffset++;
}
}
// Full pixels
$fullPixels = (int)($samplesLeft / $this->samplesPerPixel);
if ($fullPixels > 0) {
for ($i = 0; $i < $fullPixels; $i++) {
$this->_add($this->samplesPerPixel, $a, $r, $g, $b);
$this->pixelOffset++;
}
$samplesLeft -= $fullPixels * $this->samplesPerPixel;
}
// Last partial pixel
if ($samplesLeft > 0) {
$this->_add($samplesLeft, $a, $r, $g, $b);
$this->subPixelOffset += $samplesLeft;
if ($this->subPixelOffset == $this->samplesPerPixel) {
$this->subPixelOffset = 0;
$this->pixelOffset++;
}
}
}
}

View File

@@ -0,0 +1,27 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Canvas\Rasterization;
class SuperSampleRange
{
public $fromX;
public $toXExcl;
public $edges;
public $width;
public function __construct($fromX, $toXExcl)
{
$this->fromX = $fromX;
$this->toXExcl = $toXExcl;
$this->edges = array();
}
}

605
vendor/jdenticon/jdenticon/src/Color.php vendored Normal file
View File

@@ -0,0 +1,605 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon;
/**
* Represents a 24-bit color with a 8-bit alpha channel.
*/
class Color
{
private static $lightnessCompensations = array(
0.55, 0.5, 0.5, 0.46, 0.6, 0.55, 0.55);
/**
* The red component of the color in the range [0, 255].
* @var int
*/
public $r;
/**
* The green component of the color in the range [0, 255].
* @var int
*/
public $g;
/**
* The blue component of the color in the range [0, 255].
* @var int
*/
public $b;
/**
* The alpha component of the color in the range [0, 255].
* @var int
*/
public $a;
// Users of the struct should use the static factory methods
// to create Color value.
private function __construct()
{
}
/**
* Creates a Color from an RGB value.
*
* @param int $alpha Alpha channel value in the range [0, 255].
* @param int $red Red component in the range [0, 255].
* @param int $green GReen component in the range [0, 255].
* @param int $blue Blue component in the range [0, 255].
*/
public static function fromRgb($red, $green, $blue, $alpha = 255)
{
$color = new Color();
$color->r = $red;
$color->g = $green;
$color->b = $blue;
$color->a = $alpha;
return $color;
}
/**
* Creates a Color instance from HSL color parameters.
*
* @param float $hue Hue in the range [0, 1]
* @param float $saturation Saturation in the range [0, 1]
* @param float $lightness Lightness in the range [0, 1]
* @param float $alpha Alpha channel value in the range [0, 1].
*/
public static function fromHsl($hue, $saturation, $lightness, $alpha = 1.0)
{
if ($hue < 0) $hue = 0;
if ($hue > 1) $hue = 1;
if ($saturation < 0) $saturation = 0;
if ($saturation > 1) $saturation = 1;
if ($lightness < 0) $lightness = 0;
if ($lightness > 1) $lightness = 1;
if ($alpha < 0) $alpha = 0;
if ($alpha > 1) $alpha = 1;
// Based on http://www.w3.org/TR/2011/REC-css3-color-20110607/#hsl-color
if ($saturation == 0) {
$value = (int)($lightness * 255);
return self::fromRgb($value, $value, $value, (int)($alpha * 255));
} else {
if ($lightness <= 0.5) {
$m2 = $lightness * ($saturation + 1);
} else {
$m2 = $lightness + $saturation - $lightness * $saturation;
}
$m1 = $lightness * 2 - $m2;
return self::fromRgb(
self::hueToRgb($m1, $m2, $hue * 6 + 2),
self::hueToRgb($m1, $m2, $hue * 6),
self::hueToRgb($m1, $m2, $hue * 6 - 2),
(int)($alpha * 255));
}
}
/**
* Creates a Color> instance from HSL color parameters and will compensate
* the lightness for hues that appear to be darker than others.
*
* @param float $hue Hue in the range [0, 1].
* @param float $saturation Saturation in the range [0, 1].
* @param float $lightness Lightness in the range [0, 1].
* @param float $alpha Alpha channel value in the range [0, 1].
*/
public static function fromHslCompensated($hue, $saturation, $lightness, $alpha = 1.0)
{
if ($hue < 0) $hue = 0;
if ($hue > 1) $hue = 1;
$lightnessCompensation = self::$lightnessCompensations[(int)($hue * 6 + 0.5)];
// Adjust the input lightness relative to the compensation
$lightness = $lightness < 0.5 ?
$lightness * $lightnessCompensation * 2 :
$lightnessCompensation + ($lightness - 0.5) * (1 - $lightnessCompensation) * 2;
return self::fromHsl($hue, $saturation, $lightness, $alpha);
}
// Helper method for FromHsl
private static function hueToRgb($m1, $m2, $h)
{
if ($h < 0) {
$h = $h + 6;
} elseif ($h > 6) {
$h = $h - 6;
}
if ($h < 1) {
$r = $m1 + ($m2 - $m1) * $h;
} elseif ($h < 3) {
$r = $m2;
} elseif ($h < 4) {
$r = $m1 + ($m2 - $m1) * (4 - $h);
} else {
$r = $m1;
}
return (int)(255 * $r);
}
/**
* Gets the argb value of this color.
*
* @return int
*/
public function toRgba()
{
return
($this->r << 24) |
($this->g << 16) |
($this->b << 8) |
($this->a);
}
/**
* Gets a hexadecimal representation of this color on the format #rrggbbaa.
*
* @return string
*/
public function __toString()
{
return '#' . bin2hex(pack('N', $this->toRgba()));
}
/**
* Gets a hexadecimal representation of this color on the format #rrggbbaa.
*
* @return string
*/
public function toHexString($length = 8)
{
if ($length === 8) {
return $this->__toString();
}
return '#' . substr(bin2hex(pack('N', $this->toRgba())), 0, 6);
}
/**
* Tries to parse a value as a Color.
*
* @param mixed $value Value to parse.
* @throws InvalidArgumentException
* @return \Jdenticon\Color
*/
public static function parse($value) {
if ($value instanceof Color) {
return $value;
}
$value = strtolower("$value");
if (preg_match('/^#?[0-9a-f]{3,8}$/', $value) &&
self::parseHexColor($value, $result)
) {
return $result;
}
if (preg_match(
'/^rgba?\\(([^,]+),([^,]+),([^,]+)(?:,([^,]+))?\\)$/',
$value, $matches) &&
self::parseRgbComponent($matches[1], $r) &&
self::parseRgbComponent($matches[2], $g) &&
self::parseRgbComponent($matches[3], $b) &&
self::parseAlpha(isset($matches[4]) ? $matches[4] : null, $a)
) {
return self::fromRgb($r, $g, $b, (int)(255 * $a));
}
if (preg_match(
'/^hsla?\\(([^,]+),([^,]+),([^,]+)(?:,([^,]+))?\\)$/',
$value, $matches) &&
self::parseHue($matches[1], $h) &&
self::parsePercent($matches[2], $s) &&
self::parsePercent($matches[3], $l) &&
self::parseAlpha(isset($matches[4]) ? $matches[4] : null, $a)
) {
return self::fromHsl($h, $s, $l, $a);
}
$result = self::parseNamedColor($value);
if ($result !== null) {
return $result;
}
throw new \InvalidArgumentException(
"Cannot parse '$value' as a color.");
}
/**
* Parses a percent value.
*
* @param string $input Input string.
* @param float $result Resulting value in range [0, 1].
*
* @return boolean
*/
private static function parsePercent($input, &$result)
{
// Detect and remove percent sign
if (preg_match('/^\\s*(\\d*(?:\\.\\d*)?)%\\s*$/', $input, $matches)) {
$result = floatval($matches[1]) / 100;
if ($result < 0) $result = 0;
if ($result > 1) $result = 1;
return true;
}
return false;
}
/**
* Parses an alpha value.
*
* @param string $input Input string.
* @param float $result Resulting alpha in range [0, 1].
*
* @return boolean
*/
private static function parseAlpha($input, &$result)
{
if ($input === null ||
$input === ''
) {
$result = 1;
return true;
}
if (preg_match('/^\\s*(\\d*(?:\\.\\d*)?)(%?)\\s*$/', $input, $matches)) {
$result = floatval($matches[1]);
// Percentage
if ($matches[2] !== '') {
$result = $result / 100;
}
if ($result < 0) $result = 0;
if ($result > 1) $result = 1;
return true;
}
return false;
}
/**
* Parses an RGB component.
*
* @param string $input Input string.
* @param float $result Hue in range [0, 255].
*
* @return boolean
*/
private static function parseRgbComponent($input, &$result)
{
if (preg_match('/^\\s*(\\d*(?:\\.\\d*)?)(%?)\\s*$/', $input, $matches)) {
$result = floatval($matches[1]);
if ($matches[2] === '%') {
$result = 255 * $result / 100;
}
$result = (int)$result;
if ($result < 0) $result = 0;
if ($result > 255) $result = 255;
return true;
}
return false;
}
/**
* Parses a hue component.
*
* @param string $input Input string.
* @param float $result Hue in range [0, 1].
*
* @return boolean
*/
private static function parseHue($input, &$result)
{
if (preg_match(
'/^\s*(\d*(?:\.\d*)?)(deg|grad|rad|turn|)\s*$/',
$input, $matches)
) {
$result = floatval($matches[1]);
// Percentage
switch ($matches[2]) {
case "grad":
// Gradians: range 0 - 400
$result = $result / 400;
break;
case "rad":
// Radians: range 0 - 2pi
$result = $result / M_PI / 2;
break;
case "turn":
// Turns: range 0 - 1
$result = $result;
break;
default:
// Degree: range 0 - 360
$result = $result / 360;
break;
}
$result = fmod($result, 1);
if ($result < 0) {
$result += 1;
}
return true;
}
return false;
}
/**
* Parses a hex color string.
*
* @param string $input Input string.
* @param float $result Hue in range [0, 1].
*
* @return boolean
*/
private static function parseHexColor($input, &$result)
{
if ($input[0] === '#') {
$input = substr($input, 1);
}
// intval does not support unsigned 32-bit integers
// so we need to parse large numbers stepwise
$numeric24bit = intval(substr($input, 0, 6), 16);
$alpha8bit = intval(substr($input, 6, 2), 16);
switch (strlen($input)) {
case 3:
$result = self::fromRgb(
(($numeric24bit & 0xf00) >> 8) |
(($numeric24bit & 0xf00) >> 4),
(($numeric24bit & 0x0f0) >> 4) |
(($numeric24bit & 0x0f0)),
(($numeric24bit & 0x00f) << 4) |
(($numeric24bit & 0x00f))
);
return true;
case 4:
$result = self::fromRgb(
(($numeric24bit & 0xf000) >> 12) |
(($numeric24bit & 0xf000) >> 8),
(($numeric24bit & 0x0f00) >> 8) |
(($numeric24bit & 0x0f00) >> 4),
(($numeric24bit & 0x00f0) >> 4) |
(($numeric24bit & 0x00f0)),
(($numeric24bit & 0x000f) << 4) |
(($numeric24bit & 0x000f))
);
return true;
case 6:
$result = self::fromRgb(
0xff & ($numeric24bit >> 16),
0xff & ($numeric24bit >> 8),
0xff & ($numeric24bit)
);
return true;
case 8:
$result = self::fromRgb(
0xff & ($numeric24bit >> 16),
0xff & ($numeric24bit >> 8),
0xff & ($numeric24bit),
0xff & ($alpha8bit)
);
return true;
}
return false;
}
/**
* Looks up a named color to a Color instance.
*
* @param string $input Input string.
*
* @return \Jdenticon\Color
*/
private static function parseNamedColor($input)
{
// Source: https://www.w3.org/TR/css-color-4/#named-colors
switch ($input) {
case 'aliceblue': return self::fromRgb(240,248,255);
case 'antiquewhite': return self::fromRgb(250,235,215);
case 'aqua': return self::fromRgb(0,255,255);
case 'aquamarine': return self::fromRgb(127,255,212);
case 'azure': return self::fromRgb(240,255,255);
case 'beige': return self::fromRgb(245,245,220);
case 'bisque': return self::fromRgb(255,228,196);
case 'black': return self::fromRgb(0,0,0);
case 'blanchedalmond': return self::fromRgb(255,235,205);
case 'blue': return self::fromRgb(0,0,255);
case 'blueviolet': return self::fromRgb(138,43,226);
case 'brown': return self::fromRgb(165,42,42);
case 'burlywood': return self::fromRgb(222,184,135);
case 'cadetblue': return self::fromRgb(95,158,160);
case 'chartreuse': return self::fromRgb(127,255,0);
case 'chocolate': return self::fromRgb(210,105,30);
case 'coral': return self::fromRgb(255,127,80);
case 'cornflowerblue': return self::fromRgb(100,149,237);
case 'cornsilk': return self::fromRgb(255,248,220);
case 'crimson': return self::fromRgb(220,20,60);
case 'cyan': return self::fromRgb(0,255,255);
case 'darkblue': return self::fromRgb(0,0,139);
case 'darkcyan': return self::fromRgb(0,139,139);
case 'darkgoldenrod': return self::fromRgb(184,134,11);
case 'darkgray': return self::fromRgb(169,169,169);
case 'darkgreen': return self::fromRgb(0,100,0);
case 'darkgrey': return self::fromRgb(169,169,169);
case 'darkkhaki': return self::fromRgb(189,183,107);
case 'darkmagenta': return self::fromRgb(139,0,139);
case 'darkolivegreen': return self::fromRgb(85,107,47);
case 'darkorange': return self::fromRgb(255,140,0);
case 'darkorchid': return self::fromRgb(153,50,204);
case 'darkred': return self::fromRgb(139,0,0);
case 'darksalmon': return self::fromRgb(233,150,122);
case 'darkseagreen': return self::fromRgb(143,188,143);
case 'darkslateblue': return self::fromRgb(72,61,139);
case 'darkslategray': return self::fromRgb(47,79,79);
case 'darkslategrey': return self::fromRgb(47,79,79);
case 'darkturquoise': return self::fromRgb(0,206,209);
case 'darkviolet': return self::fromRgb(148,0,211);
case 'deeppink': return self::fromRgb(255,20,147);
case 'deepskyblue': return self::fromRgb(0,191,255);
case 'dimgray': return self::fromRgb(105,105,105);
case 'dimgrey': return self::fromRgb(105,105,105);
case 'dodgerblue': return self::fromRgb(30,144,255);
case 'firebrick': return self::fromRgb(178,34,34);
case 'floralwhite': return self::fromRgb(255,250,240);
case 'forestgreen': return self::fromRgb(34,139,34);
case 'fuchsia': return self::fromRgb(255,0,255);
case 'gainsboro': return self::fromRgb(220,220,220);
case 'ghostwhite': return self::fromRgb(248,248,255);
case 'gold': return self::fromRgb(255,215,0);
case 'goldenrod': return self::fromRgb(218,165,32);
case 'gray': return self::fromRgb(128,128,128);
case 'green': return self::fromRgb(0,128,0);
case 'greenyellow': return self::fromRgb(173,255,47);
case 'grey': return self::fromRgb(128,128,128);
case 'honeydew': return self::fromRgb(240,255,240);
case 'hotpink': return self::fromRgb(255,105,180);
case 'indianred': return self::fromRgb(205,92,92);
case 'indigo': return self::fromRgb(75,0,130);
case 'ivory': return self::fromRgb(255,255,240);
case 'khaki': return self::fromRgb(240,230,140);
case 'lavender': return self::fromRgb(230,230,250);
case 'lavenderblush': return self::fromRgb(255,240,245);
case 'lawngreen': return self::fromRgb(124,252,0);
case 'lemonchiffon': return self::fromRgb(255,250,205);
case 'lightblue': return self::fromRgb(173,216,230);
case 'lightcoral': return self::fromRgb(240,128,128);
case 'lightcyan': return self::fromRgb(224,255,255);
case 'lightgoldenrodyellow': return self::fromRgb(250,250,210);
case 'lightgray': return self::fromRgb(211,211,211);
case 'lightgreen': return self::fromRgb(144,238,144);
case 'lightgrey': return self::fromRgb(211,211,211);
case 'lightpink': return self::fromRgb(255,182,193);
case 'lightsalmon': return self::fromRgb(255,160,122);
case 'lightseagreen': return self::fromRgb(32,178,170);
case 'lightskyblue': return self::fromRgb(135,206,250);
case 'lightslategray': return self::fromRgb(119,136,153);
case 'lightslategrey': return self::fromRgb(119,136,153);
case 'lightsteelblue': return self::fromRgb(176,196,222);
case 'lightyellow': return self::fromRgb(255,255,224);
case 'lime': return self::fromRgb(0,255,0);
case 'limegreen': return self::fromRgb(50,205,50);
case 'linen': return self::fromRgb(250,240,230);
case 'magenta': return self::fromRgb(255,0,255);
case 'maroon': return self::fromRgb(128,0,0);
case 'mediumaquamarine': return self::fromRgb(102,205,170);
case 'mediumblue': return self::fromRgb(0,0,205);
case 'mediumorchid': return self::fromRgb(186,85,211);
case 'mediumpurple': return self::fromRgb(147,112,219);
case 'mediumseagreen': return self::fromRgb(60,179,113);
case 'mediumslateblue': return self::fromRgb(123,104,238);
case 'mediumspringgreen': return self::fromRgb(0,250,154);
case 'mediumturquoise': return self::fromRgb(72,209,204);
case 'mediumvioletred': return self::fromRgb(199,21,133);
case 'midnightblue': return self::fromRgb(25,25,112);
case 'mintcream': return self::fromRgb(245,255,250);
case 'mistyrose': return self::fromRgb(255,228,225);
case 'moccasin': return self::fromRgb(255,228,181);
case 'navajowhite': return self::fromRgb(255,222,173);
case 'navy': return self::fromRgb(0,0,128);
case 'oldlace': return self::fromRgb(253,245,230);
case 'olive': return self::fromRgb(128,128,0);
case 'olivedrab': return self::fromRgb(107,142,35);
case 'orange': return self::fromRgb(255,165,0);
case 'orangered': return self::fromRgb(255,69,0);
case 'orchid': return self::fromRgb(218,112,214);
case 'palegoldenrod': return self::fromRgb(238,232,170);
case 'palegreen': return self::fromRgb(152,251,152);
case 'paleturquoise': return self::fromRgb(175,238,238);
case 'palevioletred': return self::fromRgb(219,112,147);
case 'papayawhip': return self::fromRgb(255,239,213);
case 'peachpuff': return self::fromRgb(255,218,185);
case 'peru': return self::fromRgb(205,133,63);
case 'pink': return self::fromRgb(255,192,203);
case 'plum': return self::fromRgb(221,160,221);
case 'powderblue': return self::fromRgb(176,224,230);
case 'purple': return self::fromRgb(128,0,128);
case 'rebeccapurple': return self::fromRgb(102,51,153);
case 'red': return self::fromRgb(255,0,0);
case 'rosybrown': return self::fromRgb(188,143,143);
case 'royalblue': return self::fromRgb(65,105,225);
case 'saddlebrown': return self::fromRgb(139,69,19);
case 'salmon': return self::fromRgb(250,128,114);
case 'sandybrown': return self::fromRgb(244,164,96);
case 'seagreen': return self::fromRgb(46,139,87);
case 'seashell': return self::fromRgb(255,245,238);
case 'sienna': return self::fromRgb(160,82,45);
case 'silver': return self::fromRgb(192,192,192);
case 'skyblue': return self::fromRgb(135,206,235);
case 'slateblue': return self::fromRgb(106,90,205);
case 'slategray': return self::fromRgb(112,128,144);
case 'slategrey': return self::fromRgb(112,128,144);
case 'snow': return self::fromRgb(255,250,250);
case 'springgreen': return self::fromRgb(0,255,127);
case 'steelblue': return self::fromRgb(70,130,180);
case 'tan': return self::fromRgb(210,180,140);
case 'teal': return self::fromRgb(0,128,128);
case 'thistle': return self::fromRgb(216,191,216);
case 'tomato': return self::fromRgb(255,99,71);
case 'transparent': return self::fromRgb(0,0,0,0);
case 'turquoise': return self::fromRgb(64,224,208);
case 'violet': return self::fromRgb(238,130,238);
case 'wheat': return self::fromRgb(245,222,179);
case 'white': return self::fromRgb(255,255,255);
case 'whitesmoke': return self::fromRgb(245,245,245);
case 'yellow': return self::fromRgb(255,255,0);
case 'yellowgreen': return self::fromRgb(154,205,50);
default: return null;
}
}
}

View File

@@ -0,0 +1,492 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon;
use Jdenticon\IdenticonStyle;
use Jdenticon\Rendering\Rectangle;
use Jdenticon\Rendering\RendererInterface;
use Jdenticon\Rendering\IconGenerator;
use Jdenticon\Rendering\InternalPngRenderer;
use Jdenticon\Rendering\ImagickRenderer;
use Jdenticon\Rendering\SvgRenderer;
/**
* Represents an identicon and its style. This is the entry class to Jdenticon.
*/
class Identicon
{
/**
* @var mixed
*/
private $value;
/**
* @var boolean
*/
private $valueSet = false;
/**
* Defaults to hash of an empty string.
*
* @var string
*/
private $hash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709';
/**
* @var integer
*/
private $size = 100;
/**
* @var Jdenticon\Rendering\IconGenerator
*/
private $iconGenerator;
/**
* @var Jdenticon\IdenticonStyle
*/
private $style;
/**
* @var bool
*/
private $enableImageMagick;
/**
* Creates an Identicon instance with the specified hash.
*
* @param string $hash A binary string containing the hash that will be used
* as base for this icon. The hash must contain at least 6 bytes.
* @param int|float|double $size The size of the icon in pixels (the icon
* is quadratic).
*/
public function __construct($options = null)
{
$this->iconGenerator = IconGenerator::getDefaultGenerator();
if ($options !== null) {
$this->setOptions($options);
}
if ($this->style === null) {
$this->style = new IdenticonStyle();
}
}
/**
* Creates an Identicon instance from a specified hash.
*
* @param string $hash A binary string containing the hash that will be used
* as base for this icon. The hash must contain at least 6 bytes.
* @param int $size The size of the icon in pixels (the icon is quadratic).
* @return \Jdenticon\Identicon
*/
public static function fromHash($hash, $size)
{
return new Identicon(array('hash' => $hash, 'size' => $size));
}
/**
* Creates an Identicon instance from a specified value.
*
* @param mixed $value The value that will be used as base for this icon.
* The value will be converted to a UTF8 encoded string and then hashed
* using SHA1.
* @param int $size The size of the icon in pixels (the icon is quadratic).
* @return \Jdenticon\Identicon
*/
public static function fromValue($value, $size)
{
return new Identicon(array('value' => $value, 'size' => $size));
}
/**
* Gets an associative array of all options of this identicon.
*
* @return array
*/
public function getOptions()
{
$options = array();
if ($this->valueSet) {
$options['value'] = $this->getValue();
} elseif ($this->hash !== null) {
$options['hash'] = $this->getHash();
}
$options['size'] = $this->getSize();
$options['style'] = $this->getStyle()->getOptions();
if ($this->enableImageMagick !== null) {
$options['enableImageMagick'] = $this->getEnableImageMagick();
}
if ($this->iconGenerator !== IconGenerator::getDefaultGenerator()) {
$options['iconGenerator'] = $this->getIconGenerator();
}
return $options;
}
/**
* Sets options in this identicon by specifying an associative array of
* option values.
*
* @param array $options Options to set.
* @return self
*/
public function setOptions(array $options)
{
foreach ($options as $key => $value) {
$this->__set($key, $value);
}
return $this;
}
public function __get($name)
{
switch (strtolower($name)) {
case 'size':
return $this->getSize();
case 'hash':
return $this->getHash();
case 'value':
return $this->getValue();
case 'style':
return $this->getStyle();
case 'icongenerator':
return $this->getIconGenerator();
case 'enableimagemagick':
return $this->getEnableImageMagick();
default:
throw new \InvalidArgumentException(
"Unknown Identicon option '$name'.");
}
}
public function __set($name, $value)
{
switch (strtolower($name)) {
case 'size':
$this->setSize($value);
break;
case 'hash':
$this->setHash($value);
break;
case 'value':
$this->setValue($value);
break;
case 'style':
$this->setStyle($value);
break;
case 'icongenerator':
$this->setIconGenerator($value);
break;
case 'enableimagemagick':
$this->setEnableImageMagick($value);
break;
default:
throw new \InvalidArgumentException(
"Unknown Identicon option '$name'.");
}
}
/**
* Gets the size of the icon in pixels.
*/
public function getSize()
{
return $this->size;
}
/**
* Sets the size of this icon in pixels.
*
* @param int|float|double $size The width and height of the icon.
*/
public function setSize($size)
{
if (!is_numeric($size) || $size < 1) {
throw new \InvalidArgumentException(
"An invalid identicon size was specified. ".
"A numeric value >= 1 was expected. Specified value: $size.");
}
$this->size = (int)$size;
}
/**
* Gets the size of the icon in pixels.
*/
public function getEnableImageMagick()
{
// Enable ImageMagick on PHP < 7. On PHP 7 the performance increase
// is not as obvious as on PHP 5. Since the ImageMagick renderer has a
// lot of quirks, we don't want to use it unless really needed.
if ($this->enableImageMagick === null) {
return PHP_MAJOR_VERSION < 7 && extension_loaded('imagick');
}
return $this->enableImageMagick;
}
/**
* Sets whether ImageMagick should be used to generate PNG icons.
*
* @param bool $enable true to enable ImageMagick.
*/
public function setEnableImageMagick($enable)
{
if (!is_bool($enable)) {
throw new \InvalidArgumentException(
"enableImageMagick can only assume boolean values. Specified value: $enable.");
}
// Verify that the Imagick extension is installed
if ($enable && !extension_loaded('imagick')) {
throw new \Exception(
'Failed to enable ImageMagick. '.
'The Imagick PHP extension was not found on this system.');
}
$this->enableImageMagick = $enable;
}
/**
* Gets the {@see IconGenerator} used to generate icons.
*
* @return \Jdenticon\Rendering\IconGenerator
*/
public function getIconGenerator()
{
return $this->iconGenerator;
}
/**
* Sets the {@see IconGenerator} used to generate icons.
*
* @param \Jdenticon\Rendering\IconGenerator $iconGenerator Icon generator
* that will render the shapes of the identicon.
* @return \Jdenticon\Identicon
*/
public function setIconGenerator(IconGenerator $iconGenerator)
{
if ($iconGenerator === null) {
$iconGenerator = IconGenerator::getDefaultGenerator();
}
$this->iconGenerator = $iconGenerator;
return $this;
}
/**
* Gets or sets the style of the icon.
*
* @return \Jdenticon\IdenticonStyle
*/
public function getStyle()
{
return $this->style;
}
/**
* Gets or sets the style of the icon.
*
* @param array|\Jdenticon\IdenticonStyle $style The new style of the icon.
* NULL will revert the identicon to use the default style.
* @return self
*/
public function setStyle($style)
{
if ($style == null) {
$this->style = new IdenticonStyle();
} elseif ($style instanceof IdenticonStyle) {
$this->style = $style;
} elseif (is_array($style)) {
$this->style = new IdenticonStyle($style);
} else {
throw new \InvalidArgumentException(
"Invalid indenticon style was specified. ".
"Allowed values are IdenticonStyle instances and associative ".
"arrays containing IdenticonStyle options.");
}
return $this;
}
/**
* Gets a binary string containing the hash that is used as base for this
* icon.
*/
public function getHash()
{
return $this->hash;
}
/**
* Sets a binary string containing the hash that is used as base for this
* icon. The string should contain at least 6 bytes.
*
* @param string $hash Binary string containing the hash.
*/
public function setHash($hash)
{
if (!is_string($hash)) {
throw new \InvalidArgumentException(
'An invalid $hash was passed to Identicon. ' .
'A binary string was expected.');
}
if (strlen($hash) < 6) {
throw new \InvalidArgumentException(
'An invalid $hash was passed to Identicon. ' .
'The hash was expected to contain at least 6 bytes.');
}
$this->hash = $hash;
$this->value = null;
$this->valueSet = false;
return $this;
}
/**
* Gets a binary string containing the hash that is used as base for this
* icon.
*/
public function getValue()
{
return $this->value;
}
/**
* Sets a value that will be used as base for this icon. The value will
* be converted to a string and then hashed using SHA1.
*
* @param mixed $value Value that will be hashed.
*/
public function setValue($value)
{
$this->hash = sha1("$value");
$this->value = $value;
$this->valueSet = true;
return $this;
}
/**
* Gets the bounds of the icon excluding its padding.
*
* @return \Jdenticon\Rendering\Rectangle
*/
public function getIconBounds()
{
// Round padding to nearest integer
$padding = (int)($this->style->getPadding() * $this->size + 0.5);
return new Rectangle(
$padding, $padding,
$this->size - $padding * 2,
$this->size - $padding * 2);
}
private function getRenderer($imageFormat)
{
switch (strtolower($imageFormat)) {
case 'svg':
return new SvgRenderer($this->size, $this->size);
default:
return $this->getEnableImageMagick() ?
new ImagickRenderer($this->size, $this->size) :
new InternalPngRenderer($this->size, $this->size);
}
}
/**
* Draws this icon using a specified renderer.
*
* This method is only intended for usage with custom renderers. A custom
* renderer could as an example render an Identicon in a file format not
* natively supported by Jdenticon. To implement a new file format,
* implement {@see \Jdenticon\Rendering\RendererInterface}.
*
* @param \Jdenticon\Rendering\RendererInterface $renderer The renderer used
* to render this icon.
* @param \Jdenticon\Rendering\Rectangle $rect The bounds of the rendered
* icon. No padding will be applied to the rectangle. If the parameter
* is omitted, the rectangle is calculated from the current icon
* size and padding.
*/
public function draw(
\Jdenticon\Rendering\RendererInterface $renderer,
\Jdenticon\Rendering\Rectangle $rect = null)
{
if ($rect === null) {
$rect = $this->getIconBounds();
}
$this->iconGenerator->generate(
$renderer, $rect, $this->style, $this->hash);
}
/**
* Renders the icon directly to the page output.
*
* The method will set the 'Content-Type' HTTP header. You are recommended
* to set an appropriate 'Cache-Control' header before calling this method
* to ensure the icon is cached client side.
*
* @param string $imageFormat The image format of the output.
* Supported values are 'png' and 'svg'.
*/
public function displayImage($imageFormat = 'png')
{
$renderer = $this->getRenderer($imageFormat);
$this->draw($renderer, $this->getIconBounds());
$mimeType = $renderer->getMimeType();
$data = $renderer->getData();
header("Content-Type: $mimeType");
echo $data;
}
/**
* Renders the icon to a binary string.
*
* @param string $imageFormat The image format of the output string.
* Supported values are 'png' and 'svg'.
* @return string
*/
public function getImageData($imageFormat = 'png')
{
$renderer = $this->getRenderer($imageFormat);
$this->draw($renderer, $this->getIconBounds());
return $renderer->getData();
}
/**
* Renders the icon as a data URI. It is recommended to avoid using this
* method unless really necessary, since it will effectively disable client
* caching of generated icons, and will also cause the same icon to be
* rendered multiple times, when used multiple times on a single page.
*
* @param string $imageFormat The image format of the data URI.
* Supported values are 'png' and 'svg'.
* @return string
*/
public function getImageDataUri($imageFormat = 'png')
{
$renderer = $this->getRenderer($imageFormat);
$this->draw($renderer, $this->getIconBounds());
$mimeType = $renderer->getMimeType();
$base64 = base64_encode($renderer->getData());
return "data:$mimeType;base64,$base64";
}
}

View File

@@ -0,0 +1,460 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon;
use Jdenticon\Color;
/**
* Specifies the color style of an identicon.
*/
class IdenticonStyle
{
/**
* @var \Jdenticon\Color
*/
private $backgroundColor;
/**
* @var float
*/
private $padding;
/**
* @var float
*/
private $colorSaturation;
/**
* @var float
*/
private $grayscaleSaturation;
/**
* @var array(float)
*/
private $colorLightness;
/**
* @var array(float)
*/
private $grayscaleLightness;
/**
* @var array(integer)
*/
private $hues;
public function __construct(array $options = null)
{
$this->backgroundColor = self::getDefaultBackgroundColor();
$this->padding = self::getDefaultPadding();
$this->colorSaturation = self::getDefaultColorSaturation();
$this->grayscaleSaturation = self::getDefaultGrayscaleSaturation();
$this->colorLightness = self::getDefaultColorLightness();
$this->grayscaleLightness = self::getDefaultGrayscaleLightness();
if ($options !== null) {
$this->setOptions($options);
}
}
/**
* Gets an associative array of all options of this style.
*
* @return array
*/
public function getOptions()
{
$options = array();
$options['backgroundColor'] = $this->getBackgroundColor()->__toString();
$options['padding'] = $this->getPadding();
$options['colorSaturation'] = $this->getColorSaturation();
$options['grayscaleSaturation'] = $this->getGrayscaleSaturation();
$options['colorLightness'] = $this->getColorLightness();
$options['grayscaleLightness'] = $this->getGrayscaleLightness();
if ($this->hues !== null) {
$options['hues'] = $this->getHues();
}
return $options;
}
/**
* Sets options in this style by specifying an associative array of option
* values.
*
* @param array $options Options to set.
* @return self
*/
public function setOptions(array $options)
{
foreach ($options as $key => $value) {
$this->__set($key, $value);
}
return $this;
}
public function __get($name)
{
switch (strtolower($name)) {
case 'backgroundcolor':
return $this->getBackgroundColor();
case 'padding':
return $this->getPadding();
case 'colorsaturation':
return $this->getColorSaturation();
case 'grayscalesaturation':
return $this->getGrayscaleSaturation();
case 'colorlightness':
return $this->getColorLightness();
case 'grayscalelightness':
return $this->getGrayscaleLightness();
case 'hues':
return $this->getHues();
default:
throw new \InvalidArgumentException(
"Unknown IdenticonStyle option '$name'.");
}
}
public function __set($name, $value)
{
switch (strtolower($name)) {
case 'backgroundcolor':
$this->setBackgroundColor($value);
break;
case 'padding':
$this->setPadding($value);
break;
case 'colorsaturation':
$this->setColorSaturation($value);
break;
case 'grayscalesaturation':
$this->setGrayscaleSaturation($value);
break;
case 'colorlightness':
$this->setColorLightness($value);
break;
case 'grayscalelightness':
$this->setGrayscaleLightness($value);
break;
case 'hues':
$this->setHues($value);
break;
default:
throw new \InvalidArgumentException(
"Unknown IdenticonStyle option '$name'.");
}
}
/**
* Normalizes a hue to the first turn [0, 360).
*
* @param mixed $hue
* @return integer
*/
private static function normalizeHue($hue)
{
if (!is_numeric($hue)) {
throw new \InvalidArgumentException(
"'$hue' is not a valid hue.");
}
$hue = $hue % 360;
if ($hue < 0) {
$hue += 360;
}
return $hue;
}
/**
* Gets an array of allowed hues, or null if there are no restrictions.
*
* @return array(int)|null
*/
public function getHues()
{
return $this->hues;
}
/**
* Sets the allowed hues of generated icons.
*
* @param array(integer)|integer|null $value A hue specified in degrees,
* or an array of hues specified in degrees. If set to null, the hue
* list is cleared.
* @return self
*/
public function setHues($value)
{
$hues = array();
if ($value !== null) {
if (is_array($value)) {
foreach ($value as $hue) {
$hues[] = self::normalizeHue($hue);
}
} else {
$hues[] = self::normalizeHue($value);
}
}
$this->hues = empty($hues) ? null : $hues;
return $this;
}
/**
* Gets the padding of an icon in percents in the range [0.0, 0.4].
*
* @return float
*/
public function getPadding()
{
return $this->padding;
}
/**
* Sets the padding of an icon in percents.
*
* @param float $value New padding in the range [0.0, 0.4].
* @return self
*/
public function setPadding($value)
{
if (!is_numeric($value) || $value < 0 || $value > 0.4) {
throw new \InvalidArgumentException(
"Padding '$value' out of range. ".
"Values in the range [0.0, 0.4] are allowed.");
}
$this->padding = (float)$value;
return $this;
}
/**
* Gets the color of the identicon background.
*
* @return \Jdenticon\Color
*/
public function getBackgroundColor()
{
return $this->backgroundColor;
}
/**
* Sets the color of the identicon background.
*
* @param \Jdenticon\Color|string $value New background color.
* @return \Jdenticon\IdenticonStyle
*/
public function setBackgroundColor($value)
{
if ($value instanceof Color) {
$this->backgroundColor = $value;
} else {
$this->backgroundColor = Color::parse($value);
}
return $this;
}
/**
* Gets the saturation of the originally grayscale identicon shapes.
*
* @return float Saturation in the range [0.0, 1.0].
*/
public function getGrayscaleSaturation()
{
return $this->grayscaleSaturation;
}
/**
* Sets the saturation of the originally grayscale identicon shapes.
*
* @param $value float Saturation in the range [0.0, 1.0].
* @return self
*/
public function setGrayscaleSaturation($value)
{
if (!is_numeric($value) ||
$value < 0 || $value > 1
) {
throw new \InvalidArgumentException(
"The grayscale saturation was invalid. ".
"Only values in the range [0.0, 1.0] are allowed.");
}
$this->grayscaleSaturation = (float)$value;
return $this;
}
/**
* Gets the saturation of the colored identicon shapes.
*
* @return float Saturation in the range [0.0, 1.0].
*/
public function getColorSaturation()
{
return $this->colorSaturation;
}
/**
* Sets the saturation of the colored identicon shapes.
*
* @param $value float Saturation in the range [0.0, 1.0].
* @return self
*/
public function setColorSaturation($value)
{
if (!is_numeric($value) ||
$value < 0 || $value > 1
) {
throw new \InvalidArgumentException(
"The color saturation was invalid. ".
"Only values in the range [0.0, 1.0] are allowed.");
}
$this->colorSaturation = (float)$value;
return $this;
}
/**
* Gets the value of the ColorLightness property.
*
* @return array(float, float)
*/
public function getColorLightness()
{
return $this->colorLightness;
}
/**
* Sets the value of the ColorLightness property.
*
* @param $value array(float, float) Lightness range.
* @return self
*/
public function setColorLightness($value)
{
if (!is_array($value) ||
!array_key_exists(0, $value) ||
!array_key_exists(1, $value) ||
!is_numeric($value[0]) ||
!is_numeric($value[1]) ||
$value[0] < 0 || $value[0] > 1 ||
$value[1] < 0 || $value[1] > 1
) {
throw new \InvalidArgumentException(
"The value passed to setColorLightness was invalid. ".
"Please check the documentation.");
}
$this->colorLightness = array((float)$value[0], (float)$value[1]);
return $this;
}
/**
* Gets the value of the GrayscaleLightness property.
*
* @return array(float, float)
*/
public function getGrayscaleLightness()
{
return $this->grayscaleLightness;
}
/**
* Sets the value of the GrayscaleLightness property.
*
* @param $value array(float, float) Lightness range.
* @return self
*/
public function setGrayscaleLightness($value)
{
if (!is_array($value) ||
!array_key_exists(0, $value) ||
!array_key_exists(1, $value) ||
!is_numeric($value[0]) ||
!is_numeric($value[1]) ||
$value[0] < 0 || $value[0] > 1 ||
$value[1] < 0 || $value[1] > 1
) {
throw new \InvalidArgumentException(
"The value passed to setGrayscaleLightness was invalid. ".
"Please check the documentation.");
}
$this->grayscaleLightness = array((float)$value[0], (float)$value[1]);
return $this;
}
/**
* Gets the default value of the BackgroundColor property. Resolves to transparent.
*
* @return \Jdenticon\Color
*/
public static function getDefaultBackgroundColor()
{
return Color::fromRgb(255, 255, 255, 255);
}
/**
* Gets the default value of the Padding property. Resolves to 0.08.
*
* @return float
*/
public static function getDefaultPadding()
{
return 0.08;
}
/**
* Gets the default value of the ColorSaturation property. Resolves to 0.5.
*
* @return float
*/
public static function getDefaultColorSaturation()
{
return 0.5;
}
/**
* Gets the default value of the GrayscaleSaturation property. Resolves to 0.
*
* @return float
*/
public static function getDefaultGrayscaleSaturation()
{
return 0;
}
/**
* Gets the default value of the ColorLightness property. Resolves to [0.4, 0.8].
*
* @return array
*/
public static function getDefaultColorLightness()
{
return array(0.4, 0.8);
}
/**
* Gets the default value of the GrayscaleLightness property. Resolves to [0.3, 0.9].
*
* @return array
*/
public static function getDefaultGrayscaleLightness()
{
return array(0.3, 0.9);
}
}

View File

@@ -0,0 +1,209 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
/**
* Base class for rendering shapes in an identicon. Implement this class to e.g.
* support a new file format that is not natively supported by Jdenticon. To
* invoke the new Renderer, pass the renderer as an argument to the
* {@see \Jdenticon\Identicon::Draw} method.
*/
abstract class AbstractRenderer implements RendererInterface
{
private $transform;
protected $backgroundColor;
public function __construct()
{
$this->transform = Transform::getEmpty();
}
/**
* Sets the current transform that will be applied on all coordinates before
* being rendered to the target image.
*
* @param \Jdenticon\Rendering\Transform $transform The transform to set.
* If NULL is specified any existing transform is removed.
*/
public function setTransform(\Jdenticon\Rendering\Transform $transform)
{
$this->transform = $transform === null ?
Transform::getEmpty() : $transform;
}
/**
* Gets the current transform that will be applied on all coordinates before
* being rendered to the target image.
*
* @return \Jdenticon\Rendering\Transform
*/
public function getTransform()
{
return $this->transform;
}
/**
* Adds a polygon without translating its coordinates.
*
* @param array $points An array of the points that the polygon consists of.
*/
abstract protected function addPolygonNoTransform($points);
/**
* Adds a circle without translating its coordinates.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $size The size of the bounding rectangle.
* @param bool $counterClockwise If true the circle will be drawn
* counter clockwise.
*/
abstract protected function addCircleNoTransform($x, $y, $size, $counterClockwise);
/**
* Sets the background color of the image.
*
* @param \Jdenticon\Color $color The image background color.
*/
public function setBackgroundColor(\Jdenticon\Color $color)
{
$this->backgroundColor = $color;
}
/**
* Gets the background color of the image.
*
* @return \Jdenticon\Color
*/
public function getBackgroundColor()
{
return $this->backgroundColor;
}
private function addPolygonCore(array $points, $invert)
{
$transformedPoints = array();
foreach ($points as $point) {
$transformedPoints[] =
$this->transform->transformPoint($point->x, $point->y);
}
if ($invert) {
$transformedPoints = array_reverse($transformedPoints);
}
//var_dump($transformedPoints);
$this->addPolygonNoTransform($transformedPoints);
}
/**
* Adds a rectangle to the image.
*
* @param float $x The x-coordinate of the rectangle upper-left corner.
* @param float $y The y-coordinate of the rectangle upper-left corner.
* @param float $width The width of the rectangle.
* @param float $height The height of the rectangle.
* @param bool $invert If true the area of the rectangle will be removed
* from the filled area.
*/
public function addRectangle($x, $y, $width, $height, $invert = false)
{
$this->addPolygonCore(array(
new Point($x, $y),
new Point($x + $width, $y),
new Point($x + $width, $y + $height),
new Point($x, $y + $height),
), $invert);
}
/**
* Adds a circle to the image.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $size The size of the bounding rectangle.
* @param bool $invert If true the area of the circle will be removed
* from the filled area.
*/
public function addCircle($x, $y, $size, $invert = false)
{
$northWest = $this->transform->transformPoint($x, $y, $size, $size);
$this->addCircleNoTransform($northWest->x, $northWest->y, $size, $invert);
}
/**
* Adds a polygon to the image.
*
* @param array $points Array of points that the polygon consists of.
* @param bool $invert If true the area of the polygon will be removed
* from the filled area.
*/
public function addPolygon($points, $invert = false)
{
$this->addPolygonCore($points, $invert);
}
/**
* Adds a triangle to the image.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $width The width of the bounding rectangle.
* @param float $height The height of the bounding rectangle.
* @param float $direction The direction of the 90 degree corner of the
* triangle.
* @param bool $invert If true the area of the triangle will be removed
* from the filled area.
*/
public function addTriangle($x, $y, $width, $height, $direction, $invert = false)
{
$points = array(
new Point($x + $width, $y),
new Point($x + $width, $y + $height),
new Point($x, $y + $height),
new Point($x, $y)
);
array_splice($points, $direction, 1);
$this->addPolygonCore($points, $invert);
}
/**
* Adds a rhombus to the image.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $width The width of the bounding rectangle.
* @param float $height The height of the bounding rectangle.
* @param bool $invert If true the area of the rhombus will be removed
* from the filled area.
*/
public function addRhombus($x, $y, $width, $height, $invert = false)
{
$this->addPolygonCore(array(
new Point($x + $width / 2, $y),
new Point($x + $width, $y + $height / 2),
new Point($x + $width / 2, $y + $height),
new Point($x, $y + $height / 2),
), $invert);
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
use Jdenticon\Color;
/**
* Specifies the colors to be used in an identicon.
*/
class ColorTheme
{
private $darkGray;
private $midColor;
private $lightGray;
private $lightColor;
private $darkColor;
/**
* Creates a new ColorTheme.
*
* @param float $hue The hue of the colored shapes in the range [0, 1].
* @param \Jdenticon\IdenticonStyle $style The style that specifies the
* lightness and saturation of the icon.
*/
public function __construct($hue, \Jdenticon\IdenticonStyle $style)
{
$grayscaleLightness = $style->getGrayscaleLightness();
$colorLightness = $style->getColorLightness();
$hues = $style->getHues();
if ($hues !== null) {
// $hue is in the range [0, 1]
// Multiply with 0.999 to change the range to [0, 1)
$hueIndex = (int)($hue * 0.999 * count($hues));
$hue = (float)$hues[$hueIndex] / 360;
}
$this->darkGray = Color::fromHslCompensated(
$hue, $style->getGrayscaleSaturation(), $grayscaleLightness[0]);
$this->midColor = Color::fromHslCompensated(
$hue, $style->getColorSaturation(), ($colorLightness[0] + $colorLightness[1]) / 2);
$this->lightGray = Color::fromHslCompensated(
$hue, $style->getGrayscaleSaturation(), $grayscaleLightness[1]);
$this->lightColor = Color::fromHslCompensated(
$hue, $style->getColorSaturation(), $colorLightness[1]);
$this->darkColor = Color::fromHslCompensated(
$hue, $style->getColorSaturation(), $colorLightness[0]);
}
/**
* Gets a color from this color theme by index.
*
* @param int $index Color index in the range [0, getCount()).
* @return Jdenticon\Color
*/
public function getByIndex($index)
{
if ($index === 0) return $this->darkGray;
if ($index === 1) return $this->midColor;
if ($index === 2) return $this->lightGray;
if ($index === 3) return $this->lightColor;
if ($index === 4) return $this->darkColor;
return null;
}
/**
* Gets the number of available colors in this theme.
*
* @return int
*/
public function getCount()
{
return 5;
}
}

View File

@@ -0,0 +1,290 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
use Jdenticon\Shapes\Shape;
use Jdenticon\Shapes\ShapeCategory;
use Jdenticon\Shapes\ShapeDefinitions;
/**
* Generates identicons and render them to a
* {@link \Jdenticon\Rendering\RendererInterface}. This class dictates what
* shapes will be used in the generated icons. If you intend to customize the
* appearance of generated icons you probably wants to either subclass or modify
* this class.
*/
class IconGenerator
{
private $defaultShapes;
private static $instance;
protected function __construct()
{
$this->defaultShapes = array(
// Sides
new ShapeCategory(
/*$colorIndex=*/ 8,
/*$shapes=*/ ShapeDefinitions::getOuterShapes(),
/*$shapeIndex=*/ 2,
/*$rotationIndex=*/ 3,
/*$positions=*/ array(1,0, 2,0, 2,3, 1,3, 0,1, 3,1, 3,2, 0,2)
),
// Corners
new ShapeCategory(
/*$colorIndex=*/ 9,
/*$shapes=*/ ShapeDefinitions::getOuterShapes(),
/*$shapeIndex=*/ 4,
/*$rotationIndex=*/ 5,
/*$positions=*/ array(0,0, 3,0, 3,3, 0,3)
),
// Center
new ShapeCategory(
/*$colorIndex=*/ 10,
/*$shapes=*/ ShapeDefinitions::getCenterShapes(),
/*$shapeIndex=*/ 1,
/*$rotationIndex=*/ null,
/*$positions=*/ array(1,1, 2,1, 2,2, 1,2)
)
);
}
public static function getDefaultGenerator()
{
if (self::$instance === null) {
self::$instance = new IconGenerator();
}
return self::$instance;
}
/**
* Gets the number of cells in each direction of the icons generated by
* this IconGenerator.
*
* @return int
*/
public function getCellCount()
{
return 4;
}
/**
* Determines the hue to be used in an icon for the specified hash.
*
* @return float Hue in the range [0, 1].
*/
protected static function getHue($hash)
{
$value = hexdec(substr($hash, -7));
return $value / 0xfffffff;
}
/**
* Determines whether $newValue is duplicated in $source if all values
* in $duplicateValues are determined to be equal.
*
* @return bool
*/
private static function isDuplicate(
array $source, $newValue,
array $duplicateValues)
{
if (in_array($newValue, $duplicateValues, true)) {
foreach ($duplicateValues as $value) {
if (in_array($value, $source, true)) {
return true;
}
}
}
return false;
}
/**
* Gets the specified octet from a byte array.
*
* @param string $hash The hexstring from which the octet will be retrieved.
* @param int $index The zero-based index of the octet to be returned.
* @return int
*/
protected static function getOctet($hash, $index)
{
return hexdec($hash[$index]);
}
/**
* Gets an array of the shape categories to be rendered in icons generated
* by this IconGenerator.
*
* @return array
*/
protected function getCategories()
{
return $this->defaultShapes;
}
/**
* Gets an enumeration of individual shapes to be rendered in an icon for a
* specific hash.
*
* @param \Jdenticon\Rendering\ColorTheme $colorTheme A color theme
* specifying the colors to be used in the icon.
* @param string $hash The hash for which the shapes will be returned.
* @return array(Jdenticon\Shapes\Shape)
*/
protected function getShapes($colorTheme, $hash)
{
$usedColorThemeIndexes = array();
$categories = self::getCategories();
$shapes = array();
$colorCount = $colorTheme->getCount();
foreach ($categories as $category) {
$colorThemeIndex =
self::getOctet($hash, $category->colorIndex) % $colorCount;
if (self::isDuplicate(
// Disallow dark gray and dark color combo
$usedColorThemeIndexes, $colorThemeIndex, array(0, 4)) ||
self::isDuplicate(
// Disallow light gray and light color combo
$usedColorThemeIndexes, $colorThemeIndex, array(2, 3))
) {
$colorThemeIndex = 1;
}
$usedColorThemeIndexes[] = $colorThemeIndex;
$startRotationIndex = $category->rotationIndex === null ?
0 : self::getOctet($hash, $category->rotationIndex);
$shapeIndex =
self::getOctet($hash, $category->shapeIndex) %
count($category->shapes);
$shape = $category->shapes[$shapeIndex];
$shapes[] = new Shape(
/*$definition=*/ $shape,
/*$color=*/ $colorTheme->getByIndex($colorThemeIndex),
/*$positions=*/ $category->positions,
/*$startRotationIndex=*/ $startRotationIndex
);
}
return $shapes;
}
/**
* Creates a quadratic copy of the specified
* {@link \Jdenticon\Rendering\Rectangle} with a multiple of the cell count
* as size.
*
* @param \Jdenticon\Rendering\Rectangle $rect The rectangle to be
* normalized.
*/
protected function normalizeRectangle(\Jdenticon\Rendering\Rectangle $rect)
{
$size = (int)min($rect->width, $rect->height);
// Make size a multiple of the cell count
$size -= $size % $this->getCellCount();
return new Rectangle(
(int)($rect->x + ($rect->width - $size) / 2),
(int)($rect->y + ($rect->height - $size) / 2),
$size,
$size);
}
/**
* Renders the background of an icon.
*
* @param \Jdenticon\Rendering\RendererInterface $renderer The renderer to
* be used for rendering the icon on the target surface.
* @param \Jdenticon\Rendering\Rectangle $rect The outer bounds of the icon.
* @param \Jdenticon\IdenticonStyle $style The style of the icon.
* @param \Jdenticon\Rendering\ColorTheme $colorTheme A color theme
* specifying the colors to be used in the icon.
* @param string $hash The hash to be used as basis for the generated icon.
*/
protected function renderBackground(
\Jdenticon\Rendering\RendererInterface $renderer,
\Jdenticon\Rendering\Rectangle $rect,
\Jdenticon\IdenticonStyle $style,
\Jdenticon\Rendering\ColorTheme $colorTheme,
$hash)
{
$renderer->setBackgroundColor($style->getBackgroundColor());
}
/**
* Renders the foreground of an icon.
*
* @param \Jdenticon\Rendering\RendererInterface $renderer The renderer to
* be used for rendering the icon on the target surface.
* @param \Jdenticon\Rendering\Rectangle $rect The outer bounds of the icon.
* @param \Jdenticon\IdenticonStyle $style The style of the icon.
* @param \Jdenticon\Rendering\ColorTheme $colorTheme A color theme
* specifying the colors to be used in the icon.
* @param string $hash The hash to be used as basis for the generated icon.
*/
protected function renderForeground(
\Jdenticon\Rendering\RendererInterface $renderer,
\Jdenticon\Rendering\Rectangle $rect,
\Jdenticon\IdenticonStyle $style,
\Jdenticon\Rendering\ColorTheme $colorTheme,
$hash)
{
// Ensure rect is quadratic and a multiple of the cell count
$normalizedRect = $this->normalizeRectangle($rect);
$cellSize = $normalizedRect->width / $this->getCellCount();
foreach ($this->getShapes($colorTheme, $hash) as $shape) {
$rotation = $shape->startRotationIndex;
$renderer->beginShape($shape->color);
$positionCount = count($shape->positions);
for ($i = 0; $i + 1 < $positionCount; $i += 2) {
$renderer->setTransform(new Transform(
$normalizedRect->x + $shape->positions[$i + 0] * $cellSize,
$normalizedRect->y + $shape->positions[$i + 1] * $cellSize,
$cellSize, $rotation++ % 4));
$shape->definition->__invoke($renderer, $cellSize, $i / 2);
}
$renderer->endShape();
}
}
/**
* Generates an identicon for the specified hash.
*
* @param \Jdenticon\Rendering\RendererInterface $renderer The renderer to
* be used for rendering the icon on the target surface.
* @param \Jdenticon\Rendering\Rectangle $rect The outer bounds of the icon.
* @param \Jdenticon\IdenticonStyle $style The style of the icon.
* @param string $hash The hash to be used as basis for the generated icon.
*/
public function generate(
\Jdenticon\Rendering\RendererInterface $renderer,
\Jdenticon\Rendering\Rectangle $rect,
\Jdenticon\IdenticonStyle $style,
$hash)
{
$hue = self::getHue($hash);
$colorTheme = new ColorTheme($hue, $style);
$this->renderBackground($renderer, $rect, $style, $colorTheme, $hash);
$this->renderForeground($renderer, $rect, $style, $colorTheme, $hash);
}
}

View File

@@ -0,0 +1,296 @@
<?php
namespace Jdenticon\Rendering;
class ImagickRendererLine
{
/**
* Creates a new line from a vector.
*
* @param float $x0 Vector start x coordinate.
* @param float $y0 Vector start y coordinate.
* @param float $x1 Vector end x coordinate.
* @param float $y1 Vector end y coordinate.
*/
public static function fromVector($x0, $y0, $x1, $y1)
{
$line = new ImagickRendererLine();
$line->Px = $x0;
$line->Py = $y0;
$line->rx = $x1 - $x0;
$line->ry = $y1 - $y0;
return $line;
}
/**
* Moves the line to the right relative the direction vector.
*
* @param float $distance The number of pixels to move the line.
*/
public function moveRight($distance)
{
// Ortogonal direction vector
$rx = -$this->ry;
$ry = $this->rx;
$multiplier = $distance / sqrt($rx * $rx + $ry * $ry);
$this->Px += $rx * $multiplier;
$this->Py += $ry * $multiplier;
}
/**
* Computes the point at which two lines intersect.
*
* @return Point|null
*/
public static function intersection(
ImagickRendererLine $l1,
ImagickRendererLine $l2
) {
$rs = $l1->rx * $l2->ry - $l1->ry * $l2->rx;
if ($rs == 0) {
return null;
}
$u = (($l2->Px - $l1->Px) * $l1->ry - ($l2->Py - $l1->Py) * $l1->rx) / $rs;
return new Point(
$l2->Px + $u * $l2->rx,
$l2->Py + $u * $l2->ry
);
}
/**
* X coordiate of a point on the line.
* @var float
*/
public $Px;
/**
* Y coordiate of a point on the line.
* @var float
*/
public $Py;
/**
* X component of the direction vector.
* @var float
*/
public $rx;
/**
* Y component of the direction vector.
* @var float
*/
public $ry;
}
/**
* Renders icons as PNG using ImageMagick.
*
* Unfortunately the ImageMagick vector renderer has a lot of quirks that
* we need to accomodate. The most obvious is that the renderer always render
* all polygons 1/2 pixel too large. This behavior is documented here:
* http://www.imagemagick.org/Usage/draw/#bounds
*
* To prevent this we shrink all polygons with 1/2 pixels before passing them
* to ImageMagick.
*
* Another quirk is that if the polygon including the 1/2 pixel invisible border
* align perfectly to the pixel grid, white pixels will appear near edge
* intersections. Paths containing arcs will sometimes appear with horizontal
* lines drawn to the right side of the image.
*
* To prevent this (in most cases) we add 0.00013 to all coordinates.
*
*/
class ImagickRenderer extends AbstractRenderer
{
private $draw;
private $polygon;
private $width;
private $height;
/**
* This constant is added to all coordinates to avoid white pixels
* that sometimes appear near edge intersections when a polygon including
* its 1/2 invisible border is perfectly aligned to the pixel grid.
*/
const PREVENT_WHITE_PIXELS_OFFSET = -0.00013;
/**
* Creates an instance of the class ImagickRenderer.
*
* @param int $width The width of the icon in pixels.
* @param int $height The height of the icon in pixels.
*/
public function __construct($width, $height)
{
parent::__construct();
$this->draw = new \ImagickDraw();
$this->draw->setStrokeWidth(1);
$this->width = $width;
$this->height = $height;
}
/**
* Gets the MIME type of the renderer output.
*
* @return string
*/
public function getMimeType()
{
return 'image/png';
}
/**
* Adds a circle without translating its coordinates.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $size The size of the bounding rectangle.
* @param bool $counterClockwise If true the circle will be drawn
* counter clockwise.
*/
protected function addCircleNoTransform($x, $y, $size, $counterClockwise)
{
if ($counterClockwise) {
$x -= $size + 0.5;
$y -= 1;
} else {
$size -= 1;
$y -= 0.5;
}
$radius = $size / 2;
$this->draw->pathMoveToAbsolute(
$x + $size + self::PREVENT_WHITE_PIXELS_OFFSET,
$y + $radius + self::PREVENT_WHITE_PIXELS_OFFSET);
$this->draw->pathEllipticArcRelative($radius, $radius,
M_PI * 2, true, $counterClockwise, 0, 1);
$this->draw->pathClose();
}
/**
* Adds a polygon without translating its coordinates.
*
* @param array $points An array of the points that the polygon consists of.
*/
protected function addPolygonNoTransform($points)
{
$firstPoint = $points[0];
$lastPoint = end($points);
// Ensure polygon is closed
if ($firstPoint->x != $lastPoint->x ||
$firstPoint->y != $lastPoint->y
) {
$points[] = $firstPoint;
}
// Determine if polygon is an outer ring
// (source: https://stackoverflow.com/a/1165943)
$sum = 0;
$previousPoint = null;
foreach ($points as $point) {
if ($previousPoint !== null) {
$sum += ($point->x - $previousPoint->x) * ($point->y + $previousPoint->y);
}
$previousPoint = $point;
}
$isOuterRing = $sum < 0;
// ImageMagick draws all polygons 1 pixel too large. To prevent this,
// shrink polygons by 1 pixel.
$lines = array();
$previousPoint = null;
// Transform all edges to lines.
foreach ($points as $point) {
if ($previousPoint !== null) {
$lines[] = $line = ImagickRendererLine::fromVector(
$previousPoint->x, $previousPoint->y,
$point->x, $point->y
);
// ImageMagick draws a 1px border along the outer ring. To
// prevent the border overlaps inner rings too close to the
// outer ring, only inflate inner rings by 1/4 pixel.
$line->moveRight($isOuterRing ? 0.5 : 0.25);
}
$previousPoint = $point;
}
$first = true;
$previousLine = end($lines);
// Reconstruct point array from line intersections and draw the polygon
foreach ($lines as $line) {
$points[] = $point = ImagickRendererLine::intersection($previousLine, $line);
// Subtract 1/2 pixel to align the shapes to the pixel grid.
if ($first) {
$this->draw->pathMoveToAbsolute(
$point->x - 0.5 + self::PREVENT_WHITE_PIXELS_OFFSET,
$point->y - 0.5 + self::PREVENT_WHITE_PIXELS_OFFSET);
$first = false;
} else {
$this->draw->pathLineToAbsolute(
$point->x - 0.5 + self::PREVENT_WHITE_PIXELS_OFFSET,
$point->y - 0.5 + self::PREVENT_WHITE_PIXELS_OFFSET);
}
$previousLine = $line;
}
$this->draw->pathClose();
}
/**
* Begins a new shape. The shape should be ended with a call to endShape.
*
* @param \Jdenticon\Color $color The color of the shape.
*/
public function beginShape(\Jdenticon\Color $color)
{
$this->draw->setFillColor($color->__toString());
$this->draw->pathStart();
}
/**
* Ends the currently drawn shape.
*/
public function endShape()
{
$this->draw->pathFinish();
}
/**
* Renders this image as a PNG data stream.
*
* @return string
*/
public function getData()
{
$imagick = new \Imagick();
$imagick->newImage($this->width, $this->height, $this->backgroundColor->__toString());
$imagick->setImageFormat('png');
if (method_exists($imagick, 'setImageProperty')) {
$imagick->setImageProperty('Software', 'Jdenticon');
} else {
$imagick->setImageAttribute('Software', 'Jdenticon');
}
$imagick->drawImage($this->draw);
return $imagick->getImageBlob();
}
}

View File

@@ -0,0 +1,123 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
use Jdenticon\Canvas\Canvas;
/**
* Renders icons as PNG using the internal vector rasterizer.
*/
class InternalPngRenderer extends AbstractRenderer
{
private $canvas;
private $ctx;
/**
* Creates an instance of the class ImagickRenderer.
*
* @param int $width The width of the icon in pixels.
* @param int $height The height of the icon in pixels.
*/
public function __construct($width, $height)
{
parent::__construct();
$this->canvas = new Canvas($width, $height);
$this->ctx = $this->canvas->getContext();
}
/**
* Gets the MIME type of the renderer output.
*
* @return string
*/
public function getMimeType()
{
return 'image/png';
}
/**
* Adds a circle without translating its coordinates.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $size The size of the bounding rectangle.
* @param bool $counterClockwise If true the circle will be drawn
* counter clockwise.
*/
protected function addCircleNoTransform($x, $y, $size, $counterClockwise)
{
$radius = $size / 2;
$this->ctx->moveTo($x + $size, $y + $radius);
$this->ctx->arc(
$x + $radius, $y + $radius,
$radius, 0, M_PI * 2,
$counterClockwise);
$this->ctx->closePath();
}
/**
* Adds a polygon without translating its coordinates.
*
* @param array $points An array of the points that the polygon consists of.
*/
protected function addPolygonNoTransform($points)
{
$pointCount = count($points);
$this->ctx->moveTo($points[0]->x, $points[0]->y);
for ($i = 1; $i < $pointCount; $i++) {
$this->ctx->lineTo($points[$i]->x, $points[$i]->y);
}
$this->ctx->closePath();
}
/**
* Sets the background color of the icon.
*
* @param \Jdenticon\Color $color The background color.
*/
public function setBackgroundColor(\Jdenticon\Color $color)
{
parent::setBackgroundColor($color);
$this->canvas->backColor = $this->backgroundColor->toRgba();
}
/**
* Begins a new shape. The shape should be ended with a call to endShape.
*
* @param \Jdenticon\Color $color The color of the shape.
*/
public function beginShape(\Jdenticon\Color $color)
{
$this->ctx->fillStyle = $color->toRgba();
$this->ctx->beginPath();
}
/**
* Ends the currently drawn shape.
*/
public function endShape()
{
$this->ctx->fill();
}
/**
* Gets the output from the renderer.
*
* @return string
*/
public function getData()
{
return $this->canvas->toPng(array('Software' => 'Jdenticon'));
}
}

View File

@@ -0,0 +1,54 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
/**
* A 2D coordinate.
*/
class Point
{
/**
* Creates a new Point.
*
* @param float $x X coordinate.
* @param float $y Y coordinate.
*/
public function __construct($x, $y)
{
$this->x = $x;
$this->y = $y;
}
/**
* The X coordinate of this point.
*
* @var float
*/
public $x;
/**
* The Y coordinate of this point.
*
* @var float
*/
public $y;
/**
* Gets a string representation of the point.
*
* @return string
*/
public function __toString()
{
return $this->x + ", " + $this->y;
}
}

View File

@@ -0,0 +1,60 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
/**
* Specifies the bounds of a 2D rectangle.
*/
class Rectangle
{
/**
* The X coordinate of the left side of the rectangle.
*
* @var float
*/
public $x;
/**
* The Y coordinate of the top side of the rectangle.
*
* @var float
*/
public $y;
/**
* The width of the rectangle.
* @var float
*/
public $width;
/**
* The height of the rectangle.
* @var float
*/
public $height;
/**
* Creates a new Rectangle.
*
* @param float $x The X coordinate of the left edge of the rectangle.
* @param float $y The Y coordinate of the top edge of the rectangle.
* @param float $width The width of the rectangle.
* @param float $height The height of the rectangle.
*/
public function __construct($x, $y, $width, $height)
{
$this->x = $x;
$this->y = $y;
$this->width = $width;
$this->height = $height;
}
}

View File

@@ -0,0 +1,141 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
use Jdenticon\Color;
/**
* Interface for an identicon renderer.
*/
interface RendererInterface
{
/**
* Sets the current transform that will be applied on all coordinates before
* being rendered to the target image.
*
* @param \Jdenticon\Rendering\Transform $transform The transform to set.
* If NULL is specified any existing transform is removed.
*/
public function setTransform(\Jdenticon\Rendering\Transform $transform);
/**
* Gets the current transform that will be applied on all coordinates before
* being rendered to the target image.
*
* @return \Jdenticon\Rendering\Transform
*/
public function getTransform();
/**
* Sets the background color of the image.
*
* @param \Jdenticon\Color $color The image background color.
*/
public function setBackgroundColor(Color $color);
/**
* Gets the background color of the image.
*
* @return \Jdenticon\Color
*/
public function getBackgroundColor();
/**
* Gets the MIME type of the renderer output.
*
* @return string
*/
public function getMimeType();
/**
* Begins a new shape. The shape should be ended with a call to endShape.
*
* @param \Jdenticon\Color $color The color of the shape.
*/
public function beginShape(Color $color);
/**
* Ends the currently drawn shape.
*/
public function endShape();
/**
* Adds a rectangle to the image.
*
* @param float $x The x-coordinate of the rectangle upper-left corner.
* @param float $y The y-coordinate of the rectangle upper-left corner.
* @param float $width The width of the rectangle.
* @param float $height The height of the rectangle.
* @param bool $invert If true the area of the rectangle will be removed
* from the filled area.
*/
public function addRectangle($x, $y, $width, $height, $invert = false);
/**
* Adds a circle to the image.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $size The size of the bounding rectangle.
* @param bool $invert If true the area of the circle will be removed from
* the filled area.
*/
public function addCircle($x, $y, $size, $invert = false);
/**
* Adds a polygon to the image.
*
* @param array $points Array of points that the polygon consists of.
* @param bool $invert If true the area of the polygon will be removed from
* the filled area.
*/
public function addPolygon($points, $invert = false);
/**
* Adds a triangle to the image.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $width The width of the bounding rectangle.
* @param float $height The height of the bounding rectangle.
* @param float $direction The direction of the 90 degree corner of
* the triangle.
* @param bool $invert If true the area of the triangle will be removed
* from the filled area.
*/
public function addTriangle($x, $y, $width, $height, $direction, $invert = false);
/**
* Adds a rhombus to the image.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $width The width of the bounding rectangle.
* @param float $height The height of the bounding rectangle.
* @param bool $invert If true the area of the rhombus will be removed
* from the filled area.
*/
public function addRhombus($x, $y, $width, $height, $invert = false);
/**
* Gets the output from the renderer.
*
* @return string
*/
public function getData();
}

View File

@@ -0,0 +1,83 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
/**
* Represents a SVG path element.
*/
class SvgPath
{
private $dataString;
public function __construct()
{
$this->dataString = '';
}
/**
* Adds a circle to the SVG.
*
* @param float $x X coordinate of the left side of the containing rectangle.
* @param float $y Y coordinate of the top side of the containing rectangle.
* @param float $size The diameter of the circle.
* @param bool $counterClockwise If true the circle will be drawn counter
* clockwise. This affects the rendering since the evenodd filling rule
* is used by Jdenticon.
*/
public function addCircle($x, $y, $size, $counterClockwise)
{
$sweepFlag = $counterClockwise ? '0' : '1';
$radiusAsString = number_format($size / 2, 2, '.', '');
$this->dataString .=
'M'. number_format($x, 2, '.', '') .' '.
number_format($y + $size / 2, 2, '.', '').
'a'. $radiusAsString .','. $radiusAsString .' 0 1,'.
$sweepFlag .' '. number_format($size, 2, '.', '') .',0'.
'a'. $radiusAsString .','. $radiusAsString .' 0 1,'.
$sweepFlag .' '. number_format(-$size, 2, '.', '') .',0';
}
/**
* Adds a polygon to the SVG.
*
* @param array(\Jdenticon\Rendering\Point) $points The corners of the
* polygon.
*/
public function addPolygon($points)
{
$pointCount = count($points);
$this->dataString .= 'M'.
number_format($points[0]->x, 2, '.', '') .' '.
number_format($points[0]->y, 2, '.', '');
for ($i = 1; $i < $pointCount; $i++) {
$this->dataString .= 'L'.
number_format($points[$i]->x, 2, '.', '') .' '.
number_format($points[$i]->y, 2, '.', '');
}
$this->dataString .= 'Z';
}
/**
* Gets the path as a SVG path string.
*
* @return string
*/
public function __toString()
{
return $this->dataString;
}
}

View File

@@ -0,0 +1,133 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
/**
* Renders icons as SVG paths.
*/
class SvgRenderer extends AbstractRenderer
{
private $pathsByColor = array();
private $path;
private $width;
private $height;
/**
* Creates a new SvgRenderer.
*
* @param int $width The width of the icon in pixels.
* @param int $height The height of the icon in pixels.
*/
public function __construct($width, $height)
{
$this->width = $width;
$this->height = $height;
}
/**
* Gets the MIME type of the renderer output.
*
* @return string
*/
public function getMimeType()
{
return 'image/svg+xml';
}
/**
* Adds a circle without translating its coordinates.
*
* @param float $x The x-coordinate of the bounding rectangle
* upper-left corner.
* @param float $y The y-coordinate of the bounding rectangle
* upper-left corner.
* @param float $size The size of the bounding rectangle.
* @param bool $counterClockwise If true the circle will be drawn
* counter clockwise.
*/
protected function addCircleNoTransform($x, $y, $size, $counterClockwise)
{
$this->path->addCircle($x, $y, $size, $counterClockwise);
}
/**
* Adds a polygon without translating its coordinates.
*
* @param array $points An array of the points that the polygon consists of.
*/
protected function addPolygonNoTransform($points)
{
$this->path->addPolygon($points);
}
/**
* Begins a new shape. The shape should be ended with a call to endShape.
*
* @param \Jdenticon\Color $color The color of the shape.
*/
public function beginShape(\Jdenticon\Color $color)
{
$colorString = $color->toHexString(6);
if (isset($this->pathsByColor[$colorString])) {
$this->path = $this->pathsByColor[$colorString];
} else {
$this->path = new SvgPath();
$this->pathsByColor[$colorString] = $this->path;
}
}
/**
* Ends the currently drawn shape.
*/
public function endShape()
{
}
/**
* Generates an SVG string of the renderer output.
*
* @param bool $fragment If true an SVG string without the root svg element
* will be rendered.
*/
public function getData($fragment = false)
{
$svg = '';
$widthAsString = number_format($this->width, 0, '.', '');
$heightAsString = number_format($this->height, 0, '.', '');
if (!$fragment) {
$svg .= '<svg xmlns="http://www.w3.org/2000/svg" width="' .
$widthAsString .'" height="'. $heightAsString .'" viewBox="0 0 '.
$widthAsString .' '. $heightAsString .
'" preserveAspectRatio="xMidYMid meet">';
}
if ($this->backgroundColor->a > 0) {
$opacity = (float)$this->backgroundColor->a / 255;
$svg .= '<rect fill="'. $this->backgroundColor->toHexString(6) .
'" fill-opacity="'. number_format($opacity, 2, '.', '').
'" x="0" y="0" width="'. $widthAsString .'" height="'.
$heightAsString .'"/>';
}
foreach ($this->pathsByColor as $color => $path) {
$svg .= "<path fill=\"$color\" d=\"$path\"/>";
}
if (!$fragment) {
$svg .= '</svg>';
}
return $svg;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
/*
* Translates and rotates a point before being rendered.
*/
class Transform
{
private $x;
private $y;
private $size;
private $rotation;
/**
* Creates a new Transform.
*
* @param float $x The x-coordinate of the upper left corner of the
* transformed rectangle.
* @param float $y The y-coordinate of the upper left corner of the
* transformed rectangle.
* @param float $size The size of the transformed rectangle.
* @param integer $rotation Rotation specified as
* 0 = 0 rad, 1 = 0.5π rad, 2 = π rad, 3 = 1.5π rad.
*/
public function __construct($x, $y, $size, $rotation)
{
$this->x = $x;
$this->y = $y;
$this->size = $size;
$this->rotation = $rotation;
}
/**
* Gets a noop transform.
*
* @return \Jdenticon\Rendering\Transform
*/
public static function getEmpty()
{
return new Transform(0, 0, 0, 0);
}
/**
* Transforms the specified point based on the translation and rotation
* specification for this Transform.
*
* @param float $x x-coordinate
* @param float $y y-coordinate
* @param float $width The width of the transformed rectangle. If greater
* than 0, this will ensure the returned point is of the upper left
* corner of the transformed rectangle.
* @param float $height The height of the transformed rectangle. If greater
* than 0, this will ensure the returned point is of the upper left
* corner of the transformed rectangle.
* @return \Jdenticon\Rendering\Point
*/
public function transformPoint($x, $y, $width = 0, $height = 0)
{
$right = $this->x + $this->size;
$bottom = $this->y + $this->size;
switch ($this->rotation) {
case 1: return new Point($right - $y - $height, $this->y + $x);
case 2: return new Point($right - $x - $width, $bottom - $y - $height);
case 3: return new Point($this->x + $y, $bottom - $x - $width);
default: return new Point($this->x + $x, $this->y + $y);
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Rendering;
/**
* Specifies in what direction the 90 degree angle of a triangle is pointing.
*/
class TriangleDirection
{
/**
* The 90 degree angle is pointing to South West.
*/
const SOUTH_WEST = 0;
/**
* The 90 degree angle is pointing to North West.
*/
const NORTH_WEST = 1;
/**
* The 90 degree angle is pointing to North East.
*/
const NORTH_EAST = 2;
/**
* The 90 degree angle is pointing to South East.
*/
const SOUTH_EAST = 3;
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Shapes;
/**
* Represents a shape to be rendered in an icon. These instances are
* hash specific.
*/
class Shape
{
/**
* The shape definition to be used to render the shape.
*
* @var function(
* \Jdenticon\Rendering\RendererInterface $renderer,
* \Jdenticon\Shapes\ShapePosition $cell,
* int $index)
*/
public $definition;
/**
* The fill color of the shape.
*
* @var Jdenticon\Color
*/
public $color;
/**
* The positions in which the shape will be rendered.
*
* @var array(\Jdenticon\Shapes\ShapePosition)
*/
public $positions;
/**
* The rotation index of the icon in the first position.
*
* @var int
*/
public $startRotationIndex;
public function __construct(
$definition,
\Jdenticon\Color $color,
array $positions,
$startRotationIndex)
{
$this->definition = $definition;
$this->color = $color;
$this->positions = $positions;
$this->startRotationIndex = $startRotationIndex;
}
}

View File

@@ -0,0 +1,71 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Shapes;
/**
* Represents a category of shapes that can be rendered in an icon. These
* instances are not hash specific.
*/
class ShapeCategory
{
/**
* The index of the hash octet determining the color of shapes in this
* category.
*
* @var int
*/
public $colorIndex;
/**
* A list of possible shape definitions in this category.
*
* @var array(function(
* \Jdenticon\Rendering\RendererInterface $renderer,
* \Jdenticon\Shapes\ShapePosition $cell,
* int $index))
*/
public $shapes;
/**
* The index of the hash octet determining which of the shape definitions
* that will be used for a particular hash.
*
* @var int
*/
public $shapeIndex;
/**
* The index of the hash octet determining the rotation index of the shape
* in the first position.
*
* @var int
*/
public $rotationIndex;
/**
* The positions in which the shapes of this category will be rendered.
*
* @var array(int)
*/
public $positions;
public function __construct(
$colorIndex, array $shapes, $shapeIndex, $rotationIndex, array $positions)
{
$this->colorIndex = $colorIndex;
$this->shapes = $shapes;
$this->shapeIndex = $shapeIndex;
$this->rotationIndex = $rotationIndex;
$this->positions = $positions;
}
}

View File

@@ -0,0 +1,243 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Shapes;
use Jdenticon\Rendering\Point;
use Jdenticon\Rendering\TriangleDirection;
/**
* Provides definitions for the default shapes used in identicons.
*/
class ShapeDefinitions
{
private static $outerShapes;
private static $centerShapes;
/**
* Gets an array of all possible outer shapes. Do not modify the returned
* array.
*
* @return array(\Jdenticon\Rendering\Shape)
*/
public static function getOuterShapes()
{
if (self::$outerShapes === null) {
self::$outerShapes = self::createOuterShapes();
}
return self::$outerShapes;
}
/**
* Gets an array of all possible center shapes. Do not modify the returned
* array.
*
* @return array(\Jdenticon\Rendering\Shape)
*/
public static function getCenterShapes()
{
if (self::$centerShapes === null) {
self::$centerShapes = self::createCenterShapes();
}
return self::$centerShapes;
}
private static function createOuterShapes()
{
return array(
function ($renderer, $cell, $index)
{
$renderer->addTriangle(0, 0, $cell, $cell, 0);
},
function ($renderer, $cell, $index)
{
$renderer->addTriangle(0, $cell / 2, $cell, $cell / 2, 0);
},
function ($renderer, $cell, $index)
{
$renderer->addRhombus(0, 0, $cell, $cell);
},
function ($renderer, $cell, $index)
{
$m = $cell / 6;
$renderer->addCircle($m, $m, $cell - 2 * $m);
}
);
}
private static function createCenterShapes()
{
return array(
function ($renderer, $cell, $index)
{
$k = $cell * 0.42;
$renderer->addPolygon(array(
new Point(0, 0),
new Point($cell, 0),
new Point($cell, $cell - $k * 2),
new Point($cell - $k, $cell),
new Point(0, $cell)
));
},
function ($renderer, $cell, $index)
{
$w = (int)($cell * 0.5);
$h = (int)($cell * 0.8);
$renderer->addTriangle(
$cell - $w, 0, $w, $h,
TriangleDirection::NORTH_EAST);
},
function ($renderer, $cell, $index)
{
$s = (int)($cell / 3);
$renderer->addRectangle($s, $s, $cell - $s, $cell - $s);
},
function ($renderer, $cell, $index)
{
$tmp = $cell * 0.1;
if ($tmp > 1) {
// large icon => truncate decimals
$inner = (int)$tmp;
} elseif ($tmp > 0.5) {
// medium size icon => fixed width
$inner = 1;
} else {
// small icon => anti-aliased border
$inner = $tmp;
}
// Use fixed outer border widths in small icons to ensure
// the border is drawn
if ($cell < 6) {
$outer = 1;
} elseif ($cell < 8) {
$outer = 2;
} else {
$outer = (int)($cell / 4);
}
$renderer->addRectangle(
$outer, $outer,
$cell - $inner - $outer, $cell - $inner - $outer);
},
function ($renderer, $cell, $index)
{
$m = (int)($cell * 0.15);
$s = (int)($cell * 0.5);
$renderer->addCircle($cell - $s - $m, $cell - $s - $m, $s);
},
function ($renderer, $cell, $index)
{
$inner = $cell * 0.1;
$outer = $inner * 4;
// Align edge to nearest pixel in large icons
if ($outer > 3) {
$outer = (int)$outer;
}
$renderer->addRectangle(0, 0, $cell, $cell);
$renderer->addPolygon(array(
new Point($outer, $outer),
new Point($cell - $inner, $outer),
new Point($outer + ($cell - $outer - $inner) / 2,
$cell - $inner)
), true);
},
function ($renderer, $cell, $index)
{
$renderer->addPolygon(array(
new Point(0, 0),
new Point($cell, 0),
new Point($cell, $cell * 0.7),
new Point($cell * 0.4, $cell * 0.4),
new Point($cell * 0.7, $cell),
new Point(0, $cell)
));
},
function ($renderer, $cell, $index)
{
$renderer->addTriangle(
$cell / 2, $cell / 2, $cell / 2, $cell / 2,
TriangleDirection::SOUTH_EAST);
},
function ($renderer, $cell, $index)
{
$renderer->addPolygon(array(
new Point(0, 0),
new Point($cell, 0),
new Point($cell, $cell / 2),
new Point($cell / 2, $cell),
new Point(0, $cell)
));
},
function ($renderer, $cell, $index)
{
$tmp = $cell * 0.14;
if ($cell < 8) {
// small icon => anti-aliased border
$inner = $tmp;
} else {
// large icon => truncate decimals
$inner = (int)$tmp;
}
// Use fixed outer border widths in small icons to ensure
// the border is drawn
if ($cell < 4) {
$outer = 1;
} elseif ($cell < 6) {
$outer = 2;
} else {
$outer = (int)($cell * 0.35);
}
$renderer->addRectangle(0, 0, $cell, $cell);
$renderer->addRectangle(
$outer, $outer,
$cell - $outer - $inner, $cell - $outer - $inner, true);
},
function ($renderer, $cell, $index)
{
$inner = $cell * 0.12;
$outer = $inner * 3;
$renderer->addRectangle(0, 0, $cell, $cell);
$renderer->addCircle($outer, $outer, $cell - $inner - $outer,
true);
},
function ($renderer, $cell, $index)
{
$renderer->addTriangle(
$cell / 2, $cell / 2, $cell / 2, $cell / 2,
TriangleDirection::SOUTH_EAST);
},
function ($renderer, $cell, $index)
{
$m = $cell * 0.25;
$renderer->addRectangle(0, 0, $cell, $cell);
$renderer->addRhombus($m, $m, $cell - $m, $cell - $m, true);
},
function ($renderer, $cell, $index)
{
$m = $cell * 0.4;
$s = $cell * 1.2;
if ($index != 0) {
$renderer->addCircle($m, $m, $s);
}
}
);
}
}

View File

@@ -0,0 +1,45 @@
<?php
/**
* This file is part of Jdenticon for PHP.
* https://github.com/dmester/jdenticon-php/
*
* Copyright (c) 2018 Daniel Mester Pirttijärvi
*
* For full license information, please see the LICENSE file that was
* distributed with this source code.
*/
namespace Jdenticon\Shapes;
/**
* Specifies in which cell a shape will be rendered.
*/
class ShapePosition
{
/**
* The horizontal cell index measured left to right.
*
* @var int
*/
public $x;
/**
* The vertical cell index measured from the top.
*
* @var int
*/
public $y;
/**
* Creates a new ShapePosition instance.
*
* @param int $x The x-coordinate of the containing cell.
* @param int $y The y-coordinate of the containing cell.
*/
public function __construct($x, $y)
{
$this->x = $x;
$this->y = $y;
}
}