Merge "mediawiki.util: Don't hardcode selectors in updateTooltipAccessKeys if possible"
[lhc/web/wiklou.git] / includes / media / SVGMetadataExtractor.php
index 15cf82d..361eb94 100644 (file)
@@ -43,12 +43,19 @@ class SVGReader {
        const DEFAULT_WIDTH = 512;
        const DEFAULT_HEIGHT = 512;
        const NS_SVG = 'http://www.w3.org/2000/svg';
+       const LANG_PREFIX_MATCH = 1;
+       const LANG_FULL_MATCH = 2;
 
+       /** @var null|XMLReader */
        private $reader = null;
 
+       /** @var bool */
        private $mDebug = false;
 
+       /** @var array */
        private $metadata = array();
+       private $languages = array();
+       private $languagePrefixes = array();
 
        /**
         * Constructor
@@ -151,7 +158,9 @@ class SVGReader {
 
                        $this->debug( "$tag" );
 
-                       if ( $isSVG && $tag == 'svg' && $type == XmlReader::END_ELEMENT && $this->reader->depth <= $exitDepth ) {
+                       if ( $isSVG && $tag == 'svg' && $type == XmlReader::END_ELEMENT
+                               && $this->reader->depth <= $exitDepth
+                       ) {
                                break;
                        } elseif ( $isSVG && $tag == 'title' ) {
                                $this->readField( $tag, 'title' );
@@ -167,10 +176,8 @@ class SVGReader {
                        } elseif ( $tag !== '#text' ) {
                                $this->debug( "Unhandled top-level XML tag $tag" );
 
-                               if ( !isset( $this->metadata['animated'] ) ) {
-                                       // Recurse into children of current tag, looking for animation.
-                                       $this->animateFilter( $tag );
-                               }
+                               // Recurse into children of current tag, looking for animation and languages.
+                               $this->animateFilterAndLang( $tag );
                        }
 
                        // Goto next element, which is sibling of current (Skip children).
@@ -179,14 +186,16 @@ class SVGReader {
 
                $this->reader->close();
 
+               $this->metadata['translations'] = $this->languages + $this->languagePrefixes;
+
                return true;
        }
 
        /**
         * Read a textelement from an element
         *
-        * @param string $name of the element that we are reading from
-        * @param string $metafield that we will fill with the result
+        * @param string $name Name of the element that we are reading from
+        * @param string $metafield Field that we will fill with the result
         */
        private function readField( $name, $metafield = null ) {
                $this->debug( "Read field $metafield" );
@@ -195,7 +204,10 @@ class SVGReader {
                }
                $keepReading = $this->reader->read();
                while ( $keepReading ) {
-                       if ( $this->reader->localName == $name && $this->reader->namespaceURI == self::NS_SVG && $this->reader->nodeType == XmlReader::END_ELEMENT ) {
+                       if ( $this->reader->localName == $name
+                               && $this->reader->namespaceURI == self::NS_SVG
+                               && $this->reader->nodeType == XmlReader::END_ELEMENT
+                       ) {
                                break;
                        } elseif ( $this->reader->nodeType == XmlReader::TEXT ) {
                                $this->metadata[$metafield] = trim( $this->reader->value );
@@ -207,7 +219,7 @@ class SVGReader {
        /**
         * Read an XML snippet from an element
         *
-        * @param string $metafield that we will fill with the result
+        * @param string $metafield Field that we will fill with the result
         * @throws MWException
         */
        private function readXml( $metafield = null ) {
@@ -215,21 +227,24 @@ class SVGReader {
                if ( !$metafield || $this->reader->nodeType != XmlReader::ELEMENT ) {
                        return;
                }
-               // TODO: find and store type of xml snippet. metadata['metadataType'] = "rdf"
+               // @todo Find and store type of xml snippet. metadata['metadataType'] = "rdf"
                if ( method_exists( $this->reader, 'readInnerXML' ) ) {
                        $this->metadata[$metafield] = trim( $this->reader->readInnerXML() );
                } else {
-                       throw new MWException( "The PHP XMLReader extension does not come with readInnerXML() method. Your libxml is probably out of date (need 2.6.20 or later)." );
+                       throw new MWException( "The PHP XMLReader extension does not come " .
+                               "with readInnerXML() method. Your libxml is probably out of " .
+                               "date (need 2.6.20 or later)." );
                }
                $this->reader->next();
        }
 
        /**
-        * Filter all children, looking for animate elements
+        * Filter all children, looking for animated elements.
+        * Also get a list of languages that can be targeted.
         *
-        * @param string $name of the element that we are reading from
+        * @param string $name Name of the element that we are reading from
         */
-       private function animateFilter( $name ) {
+       private function animateFilterAndLang( $name ) {
                $this->debug( "animate filter for tag $name" );
                if ( $this->reader->nodeType != XmlReader::ELEMENT ) {
                        return;
@@ -244,7 +259,35 @@ class SVGReader {
                                && $this->reader->nodeType == XmlReader::END_ELEMENT
                        ) {
                                break;
-                       } elseif ( $this->reader->namespaceURI == self::NS_SVG && $this->reader->nodeType == XmlReader::ELEMENT ) {
+                       } elseif ( $this->reader->namespaceURI == self::NS_SVG
+                               && $this->reader->nodeType == XmlReader::ELEMENT
+                       ) {
+
+                               $sysLang = $this->reader->getAttribute( 'systemLanguage' );
+                               if ( !is_null( $sysLang ) && $sysLang !== '' ) {
+                                       // See http://www.w3.org/TR/SVG/struct.html#SystemLanguageAttribute
+                                       $langList = explode( ',', $sysLang );
+                                       foreach ( $langList as $langItem ) {
+                                               $langItem = trim( $langItem );
+                                               if ( Language::isWellFormedLanguageTag( $langItem ) ) {
+                                                       $this->languages[$langItem] = self::LANG_FULL_MATCH;
+                                               }
+                                               // Note, the standard says that any prefix should work,
+                                               // here we do only the initial prefix, since that will catch
+                                               // 99% of cases, and we are going to compare against fallbacks.
+                                               // This differs mildly from how the spec says languages should be
+                                               // handled, however it matches better how the MediaWiki language
+                                               // preference is generally handled.
+                                               $dash = strpos( $langItem, '-' );
+                                               // Intentionally checking both !false and > 0 at the same time.
+                                               if ( $dash ) {
+                                                       $itemPrefix = substr( $langItem, 0, $dash );
+                                                       if ( Language::isWellFormedLanguageTag( $itemPrefix ) ) {
+                                                               $this->languagePrefixes[$itemPrefix] = self::LANG_PREFIX_MATCH;
+                                                       }
+                                               }
+                                       }
+                               }
                                switch ( $this->reader->localName ) {
                                        case 'script':
                                                // Normally we disallow files with
@@ -265,6 +308,7 @@ class SVGReader {
                }
        }
 
+       // @todo FIXME: Unused, remove?
        private function throwXmlError( $err ) {
                $this->debug( "FAILURE: $err" );
                wfDebug( "SVGReader XML error: $err\n" );
@@ -276,10 +320,12 @@ class SVGReader {
                }
        }
 
+       // @todo FIXME: Unused, remove?
        private function warn( $data ) {
                wfDebug( "SVGReader: $data\n" );
        }
 
+       // @todo FIXME: Unused, remove?
        private function notice( $data ) {
                wfDebug( "SVGReader WARN: $data\n" );
        }
@@ -337,8 +383,8 @@ class SVGReader {
         * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers
         *
         * @param string $length CSS/SVG length.
-        * @param $viewportSize : Float optional scale for percentage units...
-        * @return float: length in pixels
+        * @param float|int $viewportSize Optional scale for percentage units...
+        * @return float Length in pixels
         */
        static function scaleSVGUnit( $length, $viewportSize = 512 ) {
                static $unitLength = array(