Brion VIBBER has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/225657

Change subject: Add ability to apply tint colors to SVGs with 'color='
......................................................................

Add ability to apply tint colors to SVGs with 'color='

\o/

Bug: T106240
Change-Id: I39bb6d47d3304f8ea6b5fb43ddc24a9d0f99e6b9
---
M includes/media/SVG.php
M languages/messages/MessagesEn.php
2 files changed, 116 insertions(+), 7 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/mediawiki/core 
refs/changes/57/225657/1

diff --git a/includes/media/SVG.php b/includes/media/SVG.php
index 1118598..3959385 100644
--- a/includes/media/SVG.php
+++ b/includes/media/SVG.php
@@ -170,6 +170,7 @@
                $physicalWidth = $params['physicalWidth'];
                $physicalHeight = $params['physicalHeight'];
                $lang = isset( $params['lang'] ) ? $params['lang'] : 
$this->getDefaultRenderLanguage( $image );
+               $color = isset( $params['color'] ) ? $params['color'] : false;
 
                if ( $flags & self::TRANSFORM_LATER ) {
                        return new ThumbnailImage( $image, $dstUrl, $dstPath, 
$params );
@@ -204,7 +205,18 @@
                // 
https://git.gnome.org/browse/librsvg/commit/?id=f01aded72c38f0e18bc7ff67dee800e380251c8e
                $tmpDir = wfTempDir() . '/svg_' . wfRandomString( 24 );
                $lnPath = "$tmpDir/" . basename( $srcPath );
-               $ok = mkdir( $tmpDir, 0771 ) && symlink( $srcPath, $lnPath );
+               $ok = mkdir( $tmpDir, 0771 );
+
+               if ( $color === false ) {
+                       // symlink awayyyyy
+                       $ok = $ok && symlink( $srcPath, $lnPath );
+               } else {
+                       // We have to modify the file to add a tint color, so 
make a modified copy instead
+                       $srcXml = file_get_contents( $srcPath );
+                       $dstXml = self::applyTintColor( $srcXml, $color );
+                       $ok = $ok && (file_put_contents( $lnPath, $dstXml ) !== 
false);
+               }
+
                /** @noinspection PhpUnusedLocalVariableInspection */
                $cleaner = new ScopedCallback( function () use ( $tmpDir, 
$lnPath ) {
                        MediaWiki\suppressWarnings();
@@ -227,6 +239,84 @@
                        return new ThumbnailImage( $image, $dstUrl, $dstPath, 
$params );
                } else {
                        return $status; // MediaTransformError
+               }
+       }
+
+       /**
+        * Take SVG source string and add a filter layer to tint to the given 
color.
+        * Useful for icons that need to be used in different color schemes.
+        *
+        * @return string
+        */
+       public static function applyTintColor( $xmlSource, $color ) {
+               $color = self::validateColor( $color );
+               $alpha = hexdec( substr( $color, 7, 2 ) ) / 255;
+               $color = substr( $color, 0, 7 );
+
+               /*
+                 <svg .... filter="url(#mw-svg-tint-filter)">
+                   <defs>
+                         <filter id="mw-svg-tint-filter">
+                           <feFlood flood-color="#8090f0" flood-opacity="1"/>
+                           <feComposite in2="SourceAlpha" operator="in"/>
+                         </filter>
+                   </defs>
+                 ..
+                 </svg>
+               */
+
+               $dom = new DomDocument();
+               MediaWiki\suppressWarnings();
+               $result = $dom->loadXML( $xmlSource );
+               MediaWiki\restoreWarnings();
+               if ( !$result ) {
+                       // unable to tint! give up
+                       return $xmlSource;
+               }
+
+               $svgns = 'http://www.w3.org/2000/svg';
+
+               $defs = $dom->documentElement->getElementsByTagNameNS( $svgns, 
'defs' );
+               if ( $defs->length == 0 ) {
+                       $def = $dom->createElementNS( $svgns, 'defs' );
+                       $dom->documentElement->insertBefore( $def, 
$dom->documentElement->firstChild );
+               } else {
+                       $def = $defs->get(0);
+               }
+
+               $filter = $dom->createElementNS( $svgns, 'filter' );
+               $filter->setAttribute( 'id', 'mw-svg-tint-filter' );
+               $feFlood = $dom->createElementNS( $svgns, 'feFlood' );
+               $feFlood->setAttribute( 'flood-color', $color );
+               $feFlood->setAttribute( 'flood-opacity', $alpha );
+               $filter->appendChild( $feFlood );
+               $feComposite = $dom->createElementNS( $svgns, 'feComposite' );
+               $feComposite->setAttribute( 'in2', 'SourceAlpha' );
+               $feComposite->setAttribute( 'operator', 'in' );
+               $filter->appendChild( $feComposite );
+               $def->appendChild( $filter );
+
+               // @todo is there a way to apply multiple filters in case one 
is already there?
+               $dom->documentElement->setAttribute( 'filter', 
'url(#mw-svg-tint-filter)' );
+
+               return $dom->saveXML();
+       }
+
+       /**
+        * @return bool|string false or validated color
+        */
+       public static function validateColor( $color ) {
+               $color = strtolower( $color );
+               if ( preg_match( '/^#([0-9a-f])([0-9a-f])([0-9a-f])$/', $color 
) ) {
+                       return $m{0} . $m{0} . $m{1} . $m{1} . $m{2} . $m{2} . 
'ff';
+               } else if ( preg_match( 
'/^#([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])$/', $color ) ) {
+                       return $m{0} . $m{0} . $m{1} . $m{1} . $m{2} . $m{2} . 
$m{3} . $m{3};
+               } else if ( preg_match( '/^#[0-9a-f]{6}$/', $color ) ) {
+                       return $color . 'ff';
+               } else if ( preg_match( '/^#[0-9a-f]{8}$/', $color ) ) {
+                       return $color;
+               } else {
+                       return false;
                }
        }
 
@@ -475,6 +565,8 @@
                        }
 
                        return true;
+               } elseif ( $name == 'color' ) {
+                       return ( $false !== self::validateColor( $value ) );
                }
 
                // Only lang, width and height are acceptable keys
@@ -487,30 +579,43 @@
         */
        function makeParamString( $params ) {
                $lang = '';
+               $color = '';
                if ( isset( $params['lang'] ) && $params['lang'] !== 'en' ) {
                        $params['lang'] = strtolower( $params['lang'] );
                        $lang = "lang{$params['lang']}-";
+               }
+               if ( isset( $params['color'] ) ) {
+                       $colorval = self::validateColor( $params['color'] );
+                       $color = 'color' . ltrim( $colorval, '#' ) . '-';
                }
                if ( !isset( $params['width'] ) ) {
                        return false;
                }
 
-               return "$lang{$params['width']}px";
+               return "$lang$color{$params['width']}px";
        }
 
        function parseParamString( $str ) {
                $m = false;
-               if ( preg_match( '/^lang([a-z]+(?:-[a-z]+)*)-(\d+)px$/', $str, 
$m ) ) {
-                       return array( 'width' => array_pop( $m ), 'lang' => 
$m[1] );
-               } elseif ( preg_match( '/^(\d+)px$/', $str, $m ) ) {
-                       return array( 'width' => $m[1], 'lang' => 'en' );
+               if ( preg_match( 
'/^(?:lang([a-z]+(?:-[a-z]+)*)-)?(?:color([0-9a-f]{6})-)?(\d+)px$/', $str, $m ) 
) {
+                       $arr = array();
+                       if ( $m[3] ) {
+                               $arr['width'] = $m[3];
+                       }
+                       if ( $m[2] ) {
+                               $arr['color'] = '#' . $m[2];
+                       }
+                       if ( $m[1] ) {
+                               $arr['lang'] = $m[1];
+                       }
+                       return $arr;
                } else {
                        return false;
                }
        }
 
        function getParamMap() {
-               return array( 'img_lang' => 'lang', 'img_width' => 'width' );
+               return array( 'img_lang' => 'lang', 'img_width' => 'width', 
'img_color' => 'color' );
        }
 
        /**
@@ -522,6 +627,9 @@
                if ( isset( $params['lang'] ) ) {
                        $scriptParams['lang'] = $params['lang'];
                }
+               if ( isset( $params['color'] ) ) {
+                       $scriptParams['color'] = $params['color'];
+               }
 
                return $scriptParams;
        }
diff --git a/languages/messages/MessagesEn.php 
b/languages/messages/MessagesEn.php
index 57a79bc..52a647b 100644
--- a/languages/messages/MessagesEn.php
+++ b/languages/messages/MessagesEn.php
@@ -275,6 +275,7 @@
        'img_framed'              => array( 1, 'framed', 'enframed', 'frame' ),
        'img_frameless'           => array( 1, 'frameless' ),
        'img_lang'                => array( 1, 'lang=$1' ),
+       'img_color'               => array( 1, 'color=$1' ),
        'img_page'                => array( 1, 'page=$1', 'page $1' ),
        'img_upright'             => array( 1, 'upright', 'upright=$1', 
'upright $1' ),
        'img_border'              => array( 1, 'border' ),

-- 
To view, visit https://gerrit.wikimedia.org/r/225657
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I39bb6d47d3304f8ea6b5fb43ddc24a9d0f99e6b9
Gerrit-PatchSet: 1
Gerrit-Project: mediawiki/core
Gerrit-Branch: master
Gerrit-Owner: Brion VIBBER <br...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to