Index: trunk/phase3/includes/media/SVGMetadataExtractor.php |
— | — | @@ -0,0 +1,107 @@ |
| 2 | +<?php |
| 3 | +/** |
| 4 | + * SVGMetadataExtractor.php |
| 5 | + * |
| 6 | + * @file |
| 7 | + * @ingroup Media |
| 8 | + */ |
| 9 | + |
| 10 | +class SVGMetadataExtractor { |
| 11 | + static function getMetadata( $filename ) { |
| 12 | + $filter = new XmlSizeFilter(); |
| 13 | + $xml = new XmlTypeCheck( $filename, array( $filter, 'filter' ) ); |
| 14 | + if( $xml->wellFormed ) { |
| 15 | + return array( |
| 16 | + 'width' => $filter->width, |
| 17 | + 'height' => $filter->height |
| 18 | + ); |
| 19 | + } |
| 20 | + } |
| 21 | +} |
| 22 | + |
| 23 | +class XmlSizeFilter { |
| 24 | + const DEFAULT_WIDTH = 512; |
| 25 | + const DEFAULT_HEIGHT = 512; |
| 26 | + var $first = true; |
| 27 | + var $width = self::DEFAULT_WIDTH; |
| 28 | + var $height = self::DEFAULT_HEIGHT; |
| 29 | + function filter( $name, $attribs ) { |
| 30 | + if( $this->first ) { |
| 31 | + $defaultWidth = self::DEFAULT_WIDTH; |
| 32 | + $defaultHeight = self::DEFAULT_HEIGHT; |
| 33 | + $aspect = 1.0; |
| 34 | + $width = null; |
| 35 | + $height = null; |
| 36 | + |
| 37 | + if( isset( $attribs['viewBox'] ) ) { |
| 38 | + // min-x min-y width height |
| 39 | + $viewBox = preg_split( '/\s+/', trim( $attribs['viewBox'] ) ); |
| 40 | + if( count( $viewBox ) == 4 ) { |
| 41 | + $viewWidth = $this->scaleSVGUnit( $viewBox[2] ); |
| 42 | + $viewHeight = $this->scaleSVGUnit( $viewBox[3] ); |
| 43 | + if( $viewWidth > 0 && $viewHeight > 0 ) { |
| 44 | + $aspect = $viewWidth / $viewHeight; |
| 45 | + $defaultHeight = $defaultWidth / $aspect; |
| 46 | + } |
| 47 | + } |
| 48 | + } |
| 49 | + if( isset( $attribs['width'] ) ) { |
| 50 | + $width = $this->scaleSVGUnit( $attribs['width'], $defaultWidth ); |
| 51 | + } |
| 52 | + if( isset( $attribs['height'] ) ) { |
| 53 | + $height = $this->scaleSVGUnit( $attribs['height'], $defaultHeight ); |
| 54 | + } |
| 55 | + |
| 56 | + if( !isset( $width ) && !isset( $height ) ) { |
| 57 | + $width = $defaultWidth; |
| 58 | + $height = $width / $aspect; |
| 59 | + } elseif( isset( $width ) && !isset( $height ) ) { |
| 60 | + $height = $width / $aspect; |
| 61 | + } elseif( isset( $height ) && !isset( $width ) ) { |
| 62 | + $width = $height * $aspect; |
| 63 | + } |
| 64 | + |
| 65 | + if( $width > 0 && $height > 0 ) { |
| 66 | + $this->width = intval( round( $width ) ); |
| 67 | + $this->height = intval( round( $height ) ); |
| 68 | + } |
| 69 | + |
| 70 | + $this->first = false; |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + /** |
| 75 | + * Return a rounded pixel equivalent for a labeled CSS/SVG length. |
| 76 | + * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers |
| 77 | + * |
| 78 | + * @param $length String: CSS/SVG length. |
| 79 | + * @param $viewportSize: Float optional scale for percentage units... |
| 80 | + * @return float: length in pixels |
| 81 | + */ |
| 82 | + function scaleSVGUnit( $length, $viewportSize=512 ) { |
| 83 | + static $unitLength = array( |
| 84 | + 'px' => 1.0, |
| 85 | + 'pt' => 1.25, |
| 86 | + 'pc' => 15.0, |
| 87 | + 'mm' => 3.543307, |
| 88 | + 'cm' => 35.43307, |
| 89 | + 'in' => 90.0, |
| 90 | + 'em' => 16.0, // fake it? |
| 91 | + 'ex' => 12.0, // fake it? |
| 92 | + '' => 1.0, // "User units" pixels by default |
| 93 | + ); |
| 94 | + $matches = array(); |
| 95 | + if( preg_match( '/^\s*(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/', $length, $matches ) ) { |
| 96 | + $length = floatval( $matches[1] ); |
| 97 | + $unit = $matches[2]; |
| 98 | + if( $unit == '%' ) { |
| 99 | + return $length * 0.01 * $viewportSize; |
| 100 | + } else { |
| 101 | + return $length * $unitLength[$unit]; |
| 102 | + } |
| 103 | + } else { |
| 104 | + // Assume pixels |
| 105 | + return floatval( $length ); |
| 106 | + } |
| 107 | + } |
| 108 | +} |
Property changes on: trunk/phase3/includes/media/SVGMetadataExtractor.php |
___________________________________________________________________ |
Added: svn:eol-style |
1 | 109 | + native |
Index: trunk/phase3/includes/media/SVG.php |
— | — | @@ -12,6 +12,8 @@ |
13 | 13 | * @ingroup Media |
14 | 14 | */ |
15 | 15 | class SvgHandler extends ImageHandler { |
| 16 | + const SVG_METADATA_VERSION = 1; |
| 17 | + |
16 | 18 | function isEnabled() { |
17 | 19 | global $wgSVGConverters, $wgSVGConverter; |
18 | 20 | if ( !isset( $wgSVGConverters[$wgSVGConverter] ) ) { |
— | — | @@ -30,6 +32,11 @@ |
31 | 33 | return true; |
32 | 34 | } |
33 | 35 | |
| 36 | + function isAnimatedImage( $image ) { |
| 37 | + # TODO: detect animated SVGs |
| 38 | + return false; |
| 39 | + } |
| 40 | + |
34 | 41 | function normaliseParams( $image, &$params ) { |
35 | 42 | global $wgSVGMaxSize; |
36 | 43 | if ( !parent::normaliseParams( $image, $params ) ) { |
— | — | @@ -111,8 +118,16 @@ |
112 | 119 | return true; |
113 | 120 | } |
114 | 121 | |
115 | | - function getImageSize( $image, $path ) { |
116 | | - return wfGetSVGsize( $path ); |
| 122 | + function getImageSize( $file, $path, $metadata = false ) { |
| 123 | + if ( $metadata === false ) { |
| 124 | + $metadata = $file->getMetaData(); |
| 125 | + } |
| 126 | + $metadata = $this->unpackMetaData( $metadata ); |
| 127 | + |
| 128 | + if ( isset( $metadata['width'] ) && isset( $metadata['height'] ) ) { |
| 129 | + return array( $metadata['width'], $metadata['height'], 'SVG', |
| 130 | + "width=\"{$metadata['width']}\" height=\"{$metadata['height']}\"" ); |
| 131 | + } |
117 | 132 | } |
118 | 133 | |
119 | 134 | function getThumbType( $ext, $mime, $params = null ) { |
— | — | @@ -126,4 +141,38 @@ |
127 | 142 | $wgLang->formatNum( $file->getHeight() ), |
128 | 143 | $wgLang->formatSize( $file->getSize() ) ); |
129 | 144 | } |
| 145 | + |
| 146 | + function formatMetadata( $file ) { |
| 147 | + return false; |
| 148 | + } |
| 149 | + |
| 150 | + function getMetadata( $file, $filename ) { |
| 151 | + $metadata = array(); |
| 152 | + try { |
| 153 | + $metadata = SVGMetadataExtractor::getMetadata( $filename ); |
| 154 | + } catch( Exception $e ) { |
| 155 | + // Broken file? |
| 156 | + wfDebug( __METHOD__ . ': ' . $e->getMessage() . "\n" ); |
| 157 | + return '0'; |
| 158 | + } |
| 159 | + $metadata['version'] = self::SVG_METADATA_VERSION; |
| 160 | + return serialize( $metadata ); |
| 161 | + } |
| 162 | + |
| 163 | + function unpackMetadata( $metadata ) { |
| 164 | + $unser = @unserialize( $metadata ); |
| 165 | + if ( isset( $unser['version'] ) && $unser['version'] == self::SVG_METADATA_VERSION ) { |
| 166 | + return $unser; |
| 167 | + } else { |
| 168 | + return false; |
| 169 | + } |
| 170 | + } |
| 171 | + |
| 172 | + function getMetadataType( $image ) { |
| 173 | + return 'parsed-svg'; |
| 174 | + } |
| 175 | + |
| 176 | + function isMetadataValid( $image, $metadata ) { |
| 177 | + return $this->unpackMetadata( $metadata ) !== false; |
| 178 | + } |
130 | 179 | } |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -483,6 +483,7 @@ |
484 | 484 | 'PNGHandler' => 'includes/media/PNG.php', |
485 | 485 | 'PNGMetadataExtractor' => 'includes/media/PNGMetadataExtractor.php', |
486 | 486 | 'SvgHandler' => 'includes/media/SVG.php', |
| 487 | + 'SVGMetadataExtractor' => 'includes/media/SVGMetadataExtractor.php', |
487 | 488 | 'ThumbnailImage' => 'includes/media/MediaTransformOutput.php', |
488 | 489 | 'TiffHandler' => 'includes/media/Tiff.php', |
489 | 490 | 'TransformParameterError' => 'includes/media/MediaTransformOutput.php', |
Index: trunk/phase3/includes/ImageFunctions.php |
— | — | @@ -6,113 +6,6 @@ |
7 | 7 | */ |
8 | 8 | |
9 | 9 | /** |
10 | | - * Return a rounded pixel equivalent for a labeled CSS/SVG length. |
11 | | - * http://www.w3.org/TR/SVG11/coords.html#UnitIdentifiers |
12 | | - * |
13 | | - * @param $length String: CSS/SVG length. |
14 | | - * @param $viewportSize: Float optional scale for percentage units... |
15 | | - * @return float: length in pixels |
16 | | - */ |
17 | | -function wfScaleSVGUnit( $length, $viewportSize=512 ) { |
18 | | - static $unitLength = array( |
19 | | - 'px' => 1.0, |
20 | | - 'pt' => 1.25, |
21 | | - 'pc' => 15.0, |
22 | | - 'mm' => 3.543307, |
23 | | - 'cm' => 35.43307, |
24 | | - 'in' => 90.0, |
25 | | - 'em' => 16.0, // fake it? |
26 | | - 'ex' => 12.0, // fake it? |
27 | | - '' => 1.0, // "User units" pixels by default |
28 | | - ); |
29 | | - $matches = array(); |
30 | | - if( preg_match( '/^\s*(\d+(?:\.\d+)?)(em|ex|px|pt|pc|cm|mm|in|%|)\s*$/', $length, $matches ) ) { |
31 | | - $length = floatval( $matches[1] ); |
32 | | - $unit = $matches[2]; |
33 | | - if( $unit == '%' ) { |
34 | | - return $length * 0.01 * $viewportSize; |
35 | | - } else { |
36 | | - return $length * $unitLength[$unit]; |
37 | | - } |
38 | | - } else { |
39 | | - // Assume pixels |
40 | | - return floatval( $length ); |
41 | | - } |
42 | | -} |
43 | | - |
44 | | -class XmlSizeFilter { |
45 | | - const DEFAULT_WIDTH = 512; |
46 | | - const DEFAULT_HEIGHT = 512; |
47 | | - var $first = true; |
48 | | - var $width = self::DEFAULT_WIDTH; |
49 | | - var $height = self::DEFAULT_HEIGHT; |
50 | | - function filter( $name, $attribs ) { |
51 | | - if( $this->first ) { |
52 | | - $defaultWidth = self::DEFAULT_WIDTH; |
53 | | - $defaultHeight = self::DEFAULT_HEIGHT; |
54 | | - $aspect = 1.0; |
55 | | - $width = null; |
56 | | - $height = null; |
57 | | - |
58 | | - if( isset( $attribs['viewBox'] ) ) { |
59 | | - // min-x min-y width height |
60 | | - $viewBox = preg_split( '/\s+/', trim( $attribs['viewBox'] ) ); |
61 | | - if( count( $viewBox ) == 4 ) { |
62 | | - $viewWidth = wfScaleSVGUnit( $viewBox[2] ); |
63 | | - $viewHeight = wfScaleSVGUnit( $viewBox[3] ); |
64 | | - if( $viewWidth > 0 && $viewHeight > 0 ) { |
65 | | - $aspect = $viewWidth / $viewHeight; |
66 | | - $defaultHeight = $defaultWidth / $aspect; |
67 | | - } |
68 | | - } |
69 | | - } |
70 | | - if( isset( $attribs['width'] ) ) { |
71 | | - $width = wfScaleSVGUnit( $attribs['width'], $defaultWidth ); |
72 | | - } |
73 | | - if( isset( $attribs['height'] ) ) { |
74 | | - $height = wfScaleSVGUnit( $attribs['height'], $defaultHeight ); |
75 | | - } |
76 | | - |
77 | | - if( !isset( $width ) && !isset( $height ) ) { |
78 | | - $width = $defaultWidth; |
79 | | - $height = $width / $aspect; |
80 | | - } elseif( isset( $width ) && !isset( $height ) ) { |
81 | | - $height = $width / $aspect; |
82 | | - } elseif( isset( $height ) && !isset( $width ) ) { |
83 | | - $width = $height * $aspect; |
84 | | - } |
85 | | - |
86 | | - if( $width > 0 && $height > 0 ) { |
87 | | - $this->width = intval( round( $width ) ); |
88 | | - $this->height = intval( round( $height ) ); |
89 | | - } |
90 | | - |
91 | | - $this->first = false; |
92 | | - } |
93 | | - } |
94 | | -} |
95 | | - |
96 | | -/** |
97 | | - * Compatible with PHP getimagesize() |
98 | | - * @todo support gzipped SVGZ |
99 | | - * @todo check XML more carefully |
100 | | - * @todo sensible defaults |
101 | | - * |
102 | | - * @param $filename String: full name of the file (passed to php fopen()). |
103 | | - * @return array |
104 | | - */ |
105 | | -function wfGetSVGsize( $filename ) { |
106 | | - $filter = new XmlSizeFilter(); |
107 | | - $xml = new XmlTypeCheck( $filename, array( $filter, 'filter' ) ); |
108 | | - if( $xml->wellFormed ) { |
109 | | - return array( $filter->width, $filter->height, 'SVG', |
110 | | - "width=\"$filter->width\" height=\"$filter->height\"" ); |
111 | | - } |
112 | | - |
113 | | - return false; |
114 | | -} |
115 | | - |
116 | | -/** |
117 | 10 | * Determine if an image exists on the 'bad image list'. |
118 | 11 | * |
119 | 12 | * The format of MediaWiki:Bad_image_list is as follows: |