From d6f2e8e9e67a242bee53c39ca711c82af54a6791 Mon Sep 17 00:00:00 2001 From: Derk-Jan Hartman Date: Mon, 1 Nov 2010 23:57:09 +0000 Subject: [PATCH] * Moving wfGetSVGSize() and wfScaleSVGUnit() into a seperate SVGMetadataExtractor. * SVG metadata is now stored and versioned. Chosing not to provide fallback for these functions as they seem unused outside of SVG.php (per Google) and grep. --- includes/AutoLoader.php | 1 + includes/ImageFunctions.php | 107 ------------------------ includes/media/SVG.php | 53 +++++++++++- includes/media/SVGMetadataExtractor.php | 107 ++++++++++++++++++++++++ 4 files changed, 159 insertions(+), 109 deletions(-) create mode 100644 includes/media/SVGMetadataExtractor.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index 9dca46d98d..8d7e76bfbe 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -483,6 +483,7 @@ $wgAutoloadLocalClasses = array( 'PNGHandler' => 'includes/media/PNG.php', 'PNGMetadataExtractor' => 'includes/media/PNGMetadataExtractor.php', 'SvgHandler' => 'includes/media/SVG.php', + 'SVGMetadataExtractor' => 'includes/media/SVGMetadataExtractor.php', 'ThumbnailImage' => 'includes/media/MediaTransformOutput.php', 'TiffHandler' => 'includes/media/Tiff.php', 'TransformParameterError' => 'includes/media/MediaTransformOutput.php', diff --git a/includes/ImageFunctions.php b/includes/ImageFunctions.php index 467624cc11..8eaebd2699 100644 --- a/includes/ImageFunctions.php +++ b/includes/ImageFunctions.php @@ -5,113 +5,6 @@ * @file */ -/** - * Return a rounded pixel equivalent for a labeled CSS/SVG length. - * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers - * - * @param $length String: CSS/SVG length. - * @param $viewportSize: Float optional scale for percentage units... - * @return float: length in pixels - */ -function wfScaleSVGUnit( $length, $viewportSize=512 ) { - static $unitLength = array( - 'px' => 1.0, - 'pt' => 1.25, - 'pc' => 15.0, - 'mm' => 3.543307, - 'cm' => 35.43307, - 'in' => 90.0, - 'em' => 16.0, // fake it? - 'ex' => 12.0, // fake it? - '' => 1.0, // "User units" pixels by default - ); - $matches = array(); - if( preg_match( '/^\s*(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/', $length, $matches ) ) { - $length = floatval( $matches[1] ); - $unit = $matches[2]; - if( $unit == '%' ) { - return $length * 0.01 * $viewportSize; - } else { - return $length * $unitLength[$unit]; - } - } else { - // Assume pixels - return floatval( $length ); - } -} - -class XmlSizeFilter { - const DEFAULT_WIDTH = 512; - const DEFAULT_HEIGHT = 512; - var $first = true; - var $width = self::DEFAULT_WIDTH; - var $height = self::DEFAULT_HEIGHT; - function filter( $name, $attribs ) { - if( $this->first ) { - $defaultWidth = self::DEFAULT_WIDTH; - $defaultHeight = self::DEFAULT_HEIGHT; - $aspect = 1.0; - $width = null; - $height = null; - - if( isset( $attribs['viewBox'] ) ) { - // min-x min-y width height - $viewBox = preg_split( '/\s+/', trim( $attribs['viewBox'] ) ); - if( count( $viewBox ) == 4 ) { - $viewWidth = wfScaleSVGUnit( $viewBox[2] ); - $viewHeight = wfScaleSVGUnit( $viewBox[3] ); - if( $viewWidth > 0 && $viewHeight > 0 ) { - $aspect = $viewWidth / $viewHeight; - $defaultHeight = $defaultWidth / $aspect; - } - } - } - if( isset( $attribs['width'] ) ) { - $width = wfScaleSVGUnit( $attribs['width'], $defaultWidth ); - } - if( isset( $attribs['height'] ) ) { - $height = wfScaleSVGUnit( $attribs['height'], $defaultHeight ); - } - - if( !isset( $width ) && !isset( $height ) ) { - $width = $defaultWidth; - $height = $width / $aspect; - } elseif( isset( $width ) && !isset( $height ) ) { - $height = $width / $aspect; - } elseif( isset( $height ) && !isset( $width ) ) { - $width = $height * $aspect; - } - - if( $width > 0 && $height > 0 ) { - $this->width = intval( round( $width ) ); - $this->height = intval( round( $height ) ); - } - - $this->first = false; - } - } -} - -/** - * Compatible with PHP getimagesize() - * @todo support gzipped SVGZ - * @todo check XML more carefully - * @todo sensible defaults - * - * @param $filename String: full name of the file (passed to php fopen()). - * @return array - */ -function wfGetSVGsize( $filename ) { - $filter = new XmlSizeFilter(); - $xml = new XmlTypeCheck( $filename, array( $filter, 'filter' ) ); - if( $xml->wellFormed ) { - return array( $filter->width, $filter->height, 'SVG', - "width=\"$filter->width\" height=\"$filter->height\"" ); - } - - return false; -} - /** * Determine if an image exists on the 'bad image list'. * diff --git a/includes/media/SVG.php b/includes/media/SVG.php index c2541659fb..38588209b4 100644 --- a/includes/media/SVG.php +++ b/includes/media/SVG.php @@ -12,6 +12,8 @@ * @ingroup Media */ class SvgHandler extends ImageHandler { + const SVG_METADATA_VERSION = 1; + function isEnabled() { global $wgSVGConverters, $wgSVGConverter; if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) { @@ -30,6 +32,11 @@ class SvgHandler extends ImageHandler { return true; } + function isAnimatedImage( $image ) { + # TODO: detect animated SVGs + return false; + } + function normaliseParams( $image, &$params ) { global $wgSVGMaxSize; if ( !parent::normaliseParams( $image, $params ) ) { @@ -111,8 +118,16 @@ class SvgHandler extends ImageHandler { return true; } - function getImageSize( $image, $path ) { - return wfGetSVGsize( $path ); + function getImageSize( $file, $path, $metadata = false ) { + if ( $metadata === false ) { + $metadata = $file->getMetaData(); + } + $metadata = $this->unpackMetaData( $metadata ); + + if ( isset( $metadata['width'] ) && isset( $metadata['height'] ) ) { + return array( $metadata['width'], $metadata['height'], 'SVG', + "width=\"{$metadata['width']}\" height=\"{$metadata['height']}\"" ); + } } function getThumbType( $ext, $mime, $params = null ) { @@ -126,4 +141,38 @@ class SvgHandler extends ImageHandler { $wgLang->formatNum( $file->getHeight() ), $wgLang->formatSize( $file->getSize() ) ); } + + function formatMetadata( $file ) { + return false; + } + + function getMetadata( $file, $filename ) { + $metadata = array(); + try { + $metadata = SVGMetadataExtractor::getMetadata( $filename ); + } catch( Exception $e ) { + // Broken file? + wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" ); + return '0'; + } + $metadata['version'] = self::SVG_METADATA_VERSION; + return serialize( $metadata ); + } + + function unpackMetadata( $metadata ) { + $unser = @unserialize( $metadata ); + if ( isset( $unser['version'] ) && $unser['version'] == self::SVG_METADATA_VERSION ) { + return $unser; + } else { + return false; + } + } + + function getMetadataType( $image ) { + return 'parsed-svg'; + } + + function isMetadataValid( $image, $metadata ) { + return $this->unpackMetadata( $metadata ) !== false; + } } diff --git a/includes/media/SVGMetadataExtractor.php b/includes/media/SVGMetadataExtractor.php new file mode 100644 index 0000000000..7862225f52 --- /dev/null +++ b/includes/media/SVGMetadataExtractor.php @@ -0,0 +1,107 @@ +wellFormed ) { + return array( + 'width' => $filter->width, + 'height' => $filter->height + ); + } + } +} + +class XmlSizeFilter { + const DEFAULT_WIDTH = 512; + const DEFAULT_HEIGHT = 512; + var $first = true; + var $width = self::DEFAULT_WIDTH; + var $height = self::DEFAULT_HEIGHT; + function filter( $name, $attribs ) { + if( $this->first ) { + $defaultWidth = self::DEFAULT_WIDTH; + $defaultHeight = self::DEFAULT_HEIGHT; + $aspect = 1.0; + $width = null; + $height = null; + + if( isset( $attribs['viewBox'] ) ) { + // min-x min-y width height + $viewBox = preg_split( '/\s+/', trim( $attribs['viewBox'] ) ); + if( count( $viewBox ) == 4 ) { + $viewWidth = $this->scaleSVGUnit( $viewBox[2] ); + $viewHeight = $this->scaleSVGUnit( $viewBox[3] ); + if( $viewWidth > 0 && $viewHeight > 0 ) { + $aspect = $viewWidth / $viewHeight; + $defaultHeight = $defaultWidth / $aspect; + } + } + } + if( isset( $attribs['width'] ) ) { + $width = $this->scaleSVGUnit( $attribs['width'], $defaultWidth ); + } + if( isset( $attribs['height'] ) ) { + $height = $this->scaleSVGUnit( $attribs['height'], $defaultHeight ); + } + + if( !isset( $width ) && !isset( $height ) ) { + $width = $defaultWidth; + $height = $width / $aspect; + } elseif( isset( $width ) && !isset( $height ) ) { + $height = $width / $aspect; + } elseif( isset( $height ) && !isset( $width ) ) { + $width = $height * $aspect; + } + + if( $width > 0 && $height > 0 ) { + $this->width = intval( round( $width ) ); + $this->height = intval( round( $height ) ); + } + + $this->first = false; + } + } + + /** + * Return a rounded pixel equivalent for a labeled CSS/SVG length. + * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers + * + * @param $length String: CSS/SVG length. + * @param $viewportSize: Float optional scale for percentage units... + * @return float: length in pixels + */ + function scaleSVGUnit( $length, $viewportSize=512 ) { + static $unitLength = array( + 'px' => 1.0, + 'pt' => 1.25, + 'pc' => 15.0, + 'mm' => 3.543307, + 'cm' => 35.43307, + 'in' => 90.0, + 'em' => 16.0, // fake it? + 'ex' => 12.0, // fake it? + '' => 1.0, // "User units" pixels by default + ); + $matches = array(); + if( preg_match( '/^\s*(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/', $length, $matches ) ) { + $length = floatval( $matches[1] ); + $unit = $matches[2]; + if( $unit == '%' ) { + return $length * 0.01 * $viewportSize; + } else { + return $length * $unitLength[$unit]; + } + } else { + // Assume pixels + return floatval( $length ); + } + } +} -- 2.20.1