jenkins-bot has submitted this change and it was merged. Change subject: Properly add stil/gd-text for ZeroBanner ......................................................................
Properly add stil/gd-text for ZeroBanner Change-Id: Idec56736150a72edc3892935d70c6847fdaf5d29 --- M composer.json M composer.lock M composer/autoload_classmap.php M composer/autoload_psr4.php M composer/installed.json A stil/gd-text/.gitignore A stil/gd-text/README.md A stil/gd-text/composer.json A stil/gd-text/examples/alignment.gif A stil/gd-text/examples/debug.png A stil/gd-text/examples/fonts.png A stil/gd-text/examples/lineheight.gif A stil/gd-text/src/Box.php A stil/gd-text/src/Color.php 14 files changed, 564 insertions(+), 2 deletions(-) Approvals: BryanDavis: Looks good to me, approved jenkins-bot: Verified diff --git a/composer.json b/composer.json index 02d8104..6065b01 100644 --- a/composer.json +++ b/composer.json @@ -32,6 +32,7 @@ "pimple/pimple": "2.1.1", "psr/log": "1.0.0", "ruflin/elastica": "3.1.1", + "stil/gd-text": "1.0.0", "symfony/process": "3.0.4", "wikimedia/assert": "0.2.2", "wikimedia/avro": "1.7.7", diff --git a/composer.lock b/composer.lock index 2400005..05b7f58 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "b17b7a811c8ff679b7c6d108c1f81e83", - "content-hash": "da2b764402ee804df059aa5368f84c2d", + "hash": "292faee4fba200fdaf2f34e66a03ff92", + "content-hash": "e83297b8a45f948622cd770f5464d102", "packages": [ { "name": "composer/semver", @@ -1158,6 +1158,37 @@ "time": "2016-03-18 07:56:36" }, { + "name": "stil/gd-text", + "version": "v1.0.0", + "source": { + "type": "git", + "url": "https://github.com/stil/gd-text.git", + "reference": "a3e561afd5a53a6a4b0c1d64ebaf51e768348a66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stil/gd-text/zipball/a3e561afd5a53a6a4b0c1d64ebaf51e768348a66", + "reference": "a3e561afd5a53a6a4b0c1d64ebaf51e768348a66", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": ">=5.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "GDText\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A class drawing multiline and aligned text on pictures. Uses GD extension.", + "time": "2015-01-02 12:21:25" + }, + { "name": "symfony/process", "version": "v3.0.4", "source": { diff --git a/composer/autoload_classmap.php b/composer/autoload_classmap.php index 46826ac..49a958f 100644 --- a/composer/autoload_classmap.php +++ b/composer/autoload_classmap.php @@ -297,6 +297,8 @@ 'Firebase\\JWT\\ExpiredException' => $vendorDir . '/firebase/php-jwt/src/ExpiredException.php', 'Firebase\\JWT\\JWT' => $vendorDir . '/firebase/php-jwt/src/JWT.php', 'Firebase\\JWT\\SignatureInvalidException' => $vendorDir . '/firebase/php-jwt/src/SignatureInvalidException.php', + 'GDText\\Box' => $vendorDir . '/stil/gd-text/src/Box.php', + 'GDText\\Color' => $vendorDir . '/stil/gd-text/src/Color.php', 'HtmlFormatter\\HtmlFormatter' => $vendorDir . '/wikimedia/html-formatter/src/HtmlFormatter.php', 'IPSet\\IPSet' => $vendorDir . '/wikimedia/ip-set/src/IPSet.php', 'JsonSchema\\Constraints\\CollectionConstraint' => $vendorDir . '/justinrainbow/json-schema/src/JsonSchema/Constraints/CollectionConstraint.php', diff --git a/composer/autoload_psr4.php b/composer/autoload_psr4.php index 4888bee..4921f11 100644 --- a/composer/autoload_psr4.php +++ b/composer/autoload_psr4.php @@ -13,6 +13,7 @@ 'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'), 'Monolog\\' => array($vendorDir . '/monolog/monolog/src/Monolog'), 'JsonSchema\\' => array($vendorDir . '/justinrainbow/json-schema/src/JsonSchema'), + 'GDText\\' => array($vendorDir . '/stil/gd-text/src'), 'Firebase\\JWT\\' => array($vendorDir . '/firebase/php-jwt/src'), 'Elastica\\' => array($vendorDir . '/ruflin/elastica/lib/Elastica'), 'Composer\\Semver\\' => array($vendorDir . '/composer/semver/src'), diff --git a/composer/installed.json b/composer/installed.json index bb5d6a4..4d5f9d5 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -2052,5 +2052,38 @@ ], "description": "Provides library of common widgets, layouts, and windows.", "homepage": "https://www.mediawiki.org/wiki/OOjs_UI" + }, + { + "name": "stil/gd-text", + "version": "v1.0.0", + "version_normalized": "1.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/stil/gd-text.git", + "reference": "a3e561afd5a53a6a4b0c1d64ebaf51e768348a66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stil/gd-text/zipball/a3e561afd5a53a6a4b0c1d64ebaf51e768348a66", + "reference": "a3e561afd5a53a6a4b0c1d64ebaf51e768348a66", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "php": ">=5.3" + }, + "time": "2015-01-02 12:21:25", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "GDText\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A class drawing multiline and aligned text on pictures. Uses GD extension." } ] diff --git a/stil/gd-text/.gitignore b/stil/gd-text/.gitignore new file mode 100644 index 0000000..654ae49 --- /dev/null +++ b/stil/gd-text/.gitignore @@ -0,0 +1,6 @@ +vendor/ +composer.lock +Thumbs.db +Desktop.ini +.DS_Store +.idea diff --git a/stil/gd-text/README.md b/stil/gd-text/README.md new file mode 100644 index 0000000..0171264 --- /dev/null +++ b/stil/gd-text/README.md @@ -0,0 +1,93 @@ +gd-text +======= + +###Basic usage example +```php +<?php +require __DIR__.'/../vendor/autoload.php'; + +use GDText\Box; +use GDText\Color; + +$im = imagecreatetruecolor(500, 500); +$backgroundColor = imagecolorallocate($im, 0, 18, 64); +imagefill($im, 0, 0, $backgroundColor); + +$box = new Box($im); +$box->setFontFace(__DIR__.'/Franchise-Bold-hinted.ttf'); // http://www.dafont.com/franchise.font +$box->setFontColor(new Color(255, 75, 140)); +$box->setTextShadow(new Color(0, 0, 0, 50), 2, 2); +$box->setFontSize(40); +$box->setBox(20, 20, 460, 460); +$box->setTextAlign('left', 'top'); +$box->draw("Franchise\nBold"); + +$box = new Box($im); +$box->setFontFace(__DIR__.'/Pacifico.ttf'); // http://www.dafont.com/pacifico.font +$box->setFontSize(80); +$box->setFontColor(new Color(255, 255, 255)); +$box->setTextShadow(new Color(0, 0, 0, 50), 0, -2); +$box->setBox(20, 20, 460, 460); +$box->setTextAlign('center', 'center'); +$box->draw("Pacifico"); + +$box = new Box($im); +$box->setFontFace(__DIR__.'/Prisma.otf'); // http://www.dafont.com/prisma.font +$box->setFontSize(70); +$box->setFontColor(new Color(148, 212, 1)); +$box->setTextShadow(new Color(0, 0, 0, 50), 0, -2); +$box->setLeading(0.7); +$box->setBox(20, 20, 460, 460); +$box->setTextAlign('right', 'bottom'); +$box->draw("Prisma"); + +header("Content-type: image/png"); +imagepng($im); +``` + +Example output: + +![fonts example](examples/fonts.png) + +###Multilined text + +```php +<?php +require __DIR__.'/../vendor/autoload.php'; + +use GDText\Box; +use GDText\Color; + +$im = imagecreatetruecolor(500, 500); +$backgroundColor = imagecolorallocate($im, 0, 18, 64); +imagefill($im, 0, 0, $backgroundColor); + +$box = new Box($im); +$box->setFontFace(__DIR__.'/Minecraftia.ttf'); // http://www.dafont.com/franchise.font +$box->setFontColor(new Color(255, 75, 140)); +$box->setTextShadow(new Color(0, 0, 0, 50), 2, 2); +$box->setFontSize(8); +$box->setLineHeight(1.5); +//$box->enableDebug(); +$box->setBox(20, 20, 460, 460); +$box->setTextAlign('left', 'top'); +$box->draw( + " Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla eleifend congue auctor. Nullam eget blandit magna. Fusce posuere lacus at orci blandit auctor. Aliquam erat volutpat. Cras pharetra aliquet leo. Cras tristique tellus sit amet vestibulum ullamcorper. Aenean quam erat, ullamcorper quis blandit id, sollicitudin lobortis orci. In non varius metus. Aenean varius porttitor augue, sit amet suscipit est posuere a. In mi leo, fermentum nec diam ut, lacinia laoreet enim. Fusce augue justo, tristique at elit ultricies, tincidunt bibendum erat.\n\n Aenean feugiat dignissim dui non scelerisque. Cras vitae rhoncus sapien. Suspendisse sed ante elit. Duis id dolor metus. Vivamus congue metus nunc, ut consequat arcu dapibus vel. Ut sed ipsum sollicitudin, rutrum quam ac, fringilla risus. Phasellus non tincidunt leo, sodales venenatis nisl. Duis lorem odio, porta quis laoreet ut, tristique a justo. Morbi dictum dictum est ut facilisis. Duis suscipit sem ligula, at commodo risus pulvinar vehicula. Sed quis quam ac quam scelerisque dapibus id non justo. Sed mollis enim id neque tempus, a congue nulla blandit. Aliquam congue convallis lacinia. Aliquam commodo eleifend nisl a consectetur.\n\n Maecenas sem nisl, adipiscing nec ante sed, sodales facilisis lectus. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Ut bibendum malesuada ipsum eget vestibulum. Pellentesque interdum tempor libero eu sagittis. Suspendisse luctus nisi ante, eget tempus erat tristique sed. Duis nec pretium velit. Praesent ornare, tortor non sagittis sollicitudin, dolor quam scelerisque risus, eu consequat magna tellus id diam. Fusce auctor ultricies arcu, vel ullamcorper dui condimentum nec. Maecenas tempus, odio non ullamcorper dignissim, tellus eros elementum turpis, quis luctus ante libero et nisi.\n\n Phasellus sed mauris vel lorem tristique tempor. Pellentesque ornare purus quis ullamcorper fermentum. Curabitur tortor mauris, semper ut erat vitae, venenatis congue eros. Ut imperdiet arcu risus, id dapibus lacus bibendum posuere. Etiam ac volutpat lectus. Vivamus in magna accumsan, dictum erat in, vehicula sem. Donec elementum lacinia fringilla. Vivamus luctus felis quis sollicitudin eleifend. Sed elementum, mi et interdum facilisis, nunc eros suscipit leo, eget convallis arcu nunc eget lectus. Quisque bibendum urna sit amet varius aliquam. In mollis ante sit amet luctus tincidunt." +); + +header("Content-type: image/png;"); +imagepng($im, null, 9, PNG_ALL_FILTERS); +``` + +![line height example](examples/lineheight.gif) + +*line height demo* + + +![align example](examples/alignment.gif) + +*text alignment demo* + +![debug example](examples/debug.png) + +*debug mode enabled demo* diff --git a/stil/gd-text/composer.json b/stil/gd-text/composer.json new file mode 100644 index 0000000..b4ab5fb --- /dev/null +++ b/stil/gd-text/composer.json @@ -0,0 +1,14 @@ +{ + "name": "stil/gd-text", + "description": "A class drawing multiline and aligned text on pictures. Uses GD extension.", + "license": "MIT", + "require": { + "php": ">=5.3", + "ext-gd": "*" + }, + "autoload": { + "psr-4": { + "GDText\\": "src/" + } + } +} \ No newline at end of file diff --git a/stil/gd-text/examples/alignment.gif b/stil/gd-text/examples/alignment.gif new file mode 100644 index 0000000..c1d87a0 --- /dev/null +++ b/stil/gd-text/examples/alignment.gif Binary files differ diff --git a/stil/gd-text/examples/debug.png b/stil/gd-text/examples/debug.png new file mode 100644 index 0000000..4646a3b --- /dev/null +++ b/stil/gd-text/examples/debug.png Binary files differ diff --git a/stil/gd-text/examples/fonts.png b/stil/gd-text/examples/fonts.png new file mode 100644 index 0000000..5afac92 --- /dev/null +++ b/stil/gd-text/examples/fonts.png Binary files differ diff --git a/stil/gd-text/examples/lineheight.gif b/stil/gd-text/examples/lineheight.gif new file mode 100644 index 0000000..a166dc2 --- /dev/null +++ b/stil/gd-text/examples/lineheight.gif Binary files differ diff --git a/stil/gd-text/src/Box.php b/stil/gd-text/src/Box.php new file mode 100644 index 0000000..4266619 --- /dev/null +++ b/stil/gd-text/src/Box.php @@ -0,0 +1,309 @@ +<?php +namespace GDText; + +class Box +{ + /** + * @var resource + */ + protected $im; + + /** + * @var int + */ + protected $fontSize = 12; + + /** + * @var Color + */ + protected $fontColor; + + /** + * @var string + */ + protected $alignX = 'left'; + + /** + * @var string + */ + protected $alignY = 'top'; + + /** + * @var float + */ + protected $lineHeight = 1.25; + + /** + * @var float + */ + protected $baseline = 0.2; + + /** + * @var string + */ + protected $fontFace = null; + + /** + * @var bool + */ + protected $debug = false; + + /** + * @var bool|array + */ + protected $textShadow = false; + + /** + * @var array + */ + protected $box = array( + 'x' => 0, + 'y' => 0, + 'width' => 100, + 'height' => 100 + ); + + public function __construct(&$image) + { + $this->im = $image; + $this->fontColor = new Color(0, 0, 0); + } + + /** + * @param Color $color Font color + */ + public function setFontColor(Color $color) + { + $this->fontColor = $color; + } + + /** + * @param string $path Path to the font file + */ + public function setFontFace($path) + { + $this->fontFace = $path; + } + + /** + * @param int $v Font size in *pixels* + */ + public function setFontSize($v) + { + $this->fontSize = $v; + } + + /** + * @param Color $color Shadow color + * @param int $xShift Relative shadow position in pixels. Positive values move shadow to right, negative to left. + * @param int $yShift Relative shadow position in pixels. Positive values move shadow to bottom, negative to up. + */ + public function setTextShadow(Color $color, $xShift, $yShift) + { + $this->textShadow = array( + 'color' => $color, + 'x' => $xShift, + 'y' => $yShift + ); + } + + /** + * Allows to customize spacing between lines. + * @param float $v Height of the single text line, in percents, proportionally to font size + */ + public function setLineHeight($v) + { + $this->lineHeight = $v; + } + + /** + * @param float $v Position of baseline, in percents, proportionally to line height measuring from the bottom. + */ + public function setBaseline($v) + { + $this->baseline = $v; + } + + /** + * Sets text alignment inside textbox + * @param string $x Horizontal alignment. Allowed values are: left, center, right. + * @param string $y Vertical alignment. Allowed values are: top, center, bottom. + */ + public function setTextAlign($x = 'left', $y = 'top') + { + $xAllowed = array('left', 'right', 'center'); + $yAllowed = array('top', 'bottom', 'center'); + + if (!in_array($x, $xAllowed)) { + throw new \InvalidArgumentException('Invalid horizontal alignement value was specified.'); + } + + if (!in_array($y, $yAllowed)) { + throw new \InvalidArgumentException('Invalid vertical alignement value was specified.'); + } + + $this->alignX = $x; + $this->alignY = $y; + } + + /** + * Sets textbox position and dimensions + * @param int $x Distance in pixels from left edge of image. + * @param int $y Distance in pixels from top edge of image. + * @param int $width Width of texbox in pixels. + * @param int $height Height of textbox in pixels. + */ + public function setBox($x, $y, $width, $height) + { + $this->box['x'] = $x; + $this->box['y'] = $y; + $this->box['width'] = $width; + $this->box['height'] = $height; + } + + /** + * Enables debug mode. Whole textbox and individual lines will be filled with random colors. + */ + public function enableDebug() + { + $this->debug = true; + } + + /** + * Draws the text on the picture. + * @param string $text Text to draw. May contain newline characters. + */ + public function draw($text) + { + if (!isset($this->fontFace)) { + throw new \InvalidArgumentException('No path to font file has been specified.'); + } + + $lines = array(); + // Split text explicitly into lines by \n, \r\n and \r + $explicitLines = preg_split('/\n|\r\n?/', $text); + foreach ($explicitLines as $line) { + // Check every line if it needs to be wrapped + $words = explode(" ", $line); + $line = $words[0]; + for ($i = 1; $i < count($words); $i++) { + $box = $this->calculateBox($line." ".$words[$i]); + if (($box[4]-$box[6]) >= $this->box['width']) { + $lines[] = $line; + $line = $words[$i]; + } else { + $line .= " ".$words[$i]; + } + } + $lines[] = $line; + } + + if ($this->debug) { + // Marks whole texbox area with color + $this->drawFilledRectangle( + $this->box['x'], + $this->box['y'], + $this->box['width'], + $this->box['height'], + new Color(rand(180, 255), rand(180, 255), rand(180, 255), 80) + ); + } + + $lineHeightPx = $this->lineHeight * $this->fontSize; + $textHeight = count($lines) * $lineHeightPx; + + switch ($this->alignY) { + case 'center': + $yAlign = ($this->box['height'] / 2) - ($textHeight / 2); + break; + case 'bottom': + $yAlign = $this->box['height'] - $textHeight; + break; + case 'top': + default: + $yAlign = 0; + } + + $n = 0; + foreach ($lines as $line) { + $box = $this->calculateBox($line); + $boxWidth = $box[2] - $box[0]; + switch ($this->alignX) { + case 'center': + $xAlign = ($this->box['width'] - $boxWidth) / 2; + break; + case 'right': + $xAlign = ($this->box['width'] - $boxWidth); + break; + case 'left': + default: + $xAlign = 0; + } + $yShift = $lineHeightPx * (1 - $this->baseline); + + // current line X and Y position + $xMOD = $this->box['x'] + $xAlign; + $yMOD = $this->box['y'] + $yAlign + $yShift + ($n * $lineHeightPx); + + if ($this->debug) { + // Marks current line with color + $this->drawFilledRectangle( + $xMOD, + $this->box['y'] + $yAlign + ($n * $lineHeightPx), + $boxWidth, + $lineHeightPx, + new Color(rand(1, 180), rand(1, 180), rand(1, 180)) + ); + } + + if ($this->textShadow !== false) { + $this->drawInternal( + $xMOD + $this->textShadow['x'], + $yMOD + $this->textShadow['y'], + $this->textShadow['color'], + $line + ); + } + + $this->drawInternal( + $xMOD, + $yMOD, + $this->fontColor, + $line + ); + + $n++; + } + } + + protected function getFontSizeInPoints() + { + return 0.75 * $this->fontSize; + } + + protected function drawFilledRectangle($x, $y, $width, $height, Color $color) + { + imagefilledrectangle($this->im, $x, $y, $x + $width, $y + $height, + $color->getIndex($this->im) + ); + } + + protected function calculateBox($text) + { + return imageftbbox($this->getFontSizeInPoints(), 0, $this->fontFace, $text); + } + + protected function drawInternal($x, $y, Color $color, $text) + { + imagefttext( + $this->im, + $this->getFontSizeInPoints(), + 0, // no rotation + $x, + $y, + $color->getIndex($this->im), + $this->fontFace, + $text + ); + } +} diff --git a/stil/gd-text/src/Color.php b/stil/gd-text/src/Color.php new file mode 100644 index 0000000..10238b9 --- /dev/null +++ b/stil/gd-text/src/Color.php @@ -0,0 +1,72 @@ +<?php +namespace GDText; + +class Color +{ + /** + * @var int + */ + protected $red; + + /** + * @var int + */ + protected $green; + + /** + * @var int + */ + protected $blue; + + /** + * @var int|null + */ + protected $alpha; + + /** + * @param int $red Value of red component 0-255 + * @param int $green Value of green component 0-255 + * @param int $blue Value of blue component 0-255 + * @param int $alpha A value between 0 and 127. 0 indicates completely opaque while 127 indicates completely transparent. + */ + public function __construct($red = 0, $green = 0, $blue = 0, $alpha = null) + { + $this->red = $red; + $this->green = $green; + $this->blue = $blue; + $this->alpha = $alpha; + } + + /** + * @param resource $image GD image resource + * @return int Returns the index of the specified color+alpha in the palette of the image, + * or -1 if the color does not exist in the image's palette. + */ + public function getIndex($image) + { + if ($this->hasAlphaChannel()) { + return imagecolorexactalpha( + $image, + $this->red, + $this->green, + $this->blue, + $this->alpha + ); + } else { + return imagecolorexact( + $image, + $this->red, + $this->green, + $this->blue + ); + } + } + + /** + * @return bool TRUE when alpha channel is specified, FALSE otherwise + */ + public function hasAlphaChannel() + { + return $this->alpha !== null; + } +} \ No newline at end of file -- To view, visit https://gerrit.wikimedia.org/r/319765 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Idec56736150a72edc3892935d70c6847fdaf5d29 Gerrit-PatchSet: 2 Gerrit-Project: mediawiki/vendor Gerrit-Branch: master Gerrit-Owner: Reedy <re...@wikimedia.org> Gerrit-Reviewer: BryanDavis <bda...@wikimedia.org> Gerrit-Reviewer: Legoktm <lego...@member.fsf.org> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits