for 'languageScripts'.
* Added a new hook, "ContentAlterParserOutput", to allow extensions to modify the
parser output for a content object before links update.
+* (bug 67341) SVG images will no longer be base64-encoded when being embedded
+ in CSS. This results in slight size increase before gzip compression (due to
+ percent-encoding), but up to 20% decrease after it.
=== Bug fixes in 1.25 ===
* which when base64 encoded will result in a 1/3 increase in size.
*/
const EMBED_SIZE_LIMIT = 24576;
+ const DATA_URI_SIZE_LIMIT = 32768;
const URL_REGEX = 'url\(\s*[\'"]?(?P<file>[^\?\)\'"]*?)(?P<query>\?[^\)\'"]*?|)[\'"]?\s*\)';
const EMBED_REGEX = '\/\*\s*\@embed\s*\*\/';
const COMMENT_REGEX = '\/\*.*?\*\/';
}
/**
- * Encode an image file as a base64 data URI.
- * If the image file has a suitable MIME type and size, encode it as a
- * base64 data URI. Return false if the image type is unfamiliar or exceeds
- * the size limit.
+ * Encode an image file as a data URI.
+ *
+ * If the image file has a suitable MIME type and size, encode it as a data URI, base64-encoded
+ * for binary files or just percent-encoded otherwise. Return false if the image type is
+ * unfamiliar or file exceeds the size limit.
*
* @param string $file Image file to encode.
* @param string|null $type File's MIME type or null. If null, CSSMin will
* @param int|bool $sizeLimit If the size of the target file is greater than
* this value, decline to encode the image file and return false
* instead. If $sizeLimit is false, no limit is enforced.
- * @return string|bool: Image contents encoded as a data URI or false.
+ * @return string|bool Image contents encoded as a data URI or false.
*/
public static function encodeImageAsDataURI( $file, $type = null,
$sizeLimit = self::EMBED_SIZE_LIMIT
if ( !$type ) {
return false;
}
- $data = base64_encode( file_get_contents( $file ) );
- return 'data:' . $type . ';base64,' . $data;
+
+ $contents = file_get_contents( $file );
+ // Only whitespace and printable ASCII characters
+ $isText = (bool)preg_match( '/^[\r\n\t\x20-\x7e]+$/', $contents );
+
+ if ( $isText ) {
+ // Do not base64-encode non-binary files (sane SVGs), unless that'd exceed URI length limit.
+ // (This often produces longer URLs, but they compress better, yielding a net smaller size.)
+ $uri = 'data:' . $type . ',' . rawurlencode( $contents );
+ if ( strlen( $uri ) >= self::DATA_URI_SIZE_LIMIT ) {
+ $uri = 'data:' . $type . ';base64,' . base64_encode( $contents );
+ }
+ } else {
+ $uri = 'data:' . $type . ';base64,' . base64_encode( $contents );
+ }
+
+ return $uri;
}
/**
// Full paths start with http://localhost/w/.
// Timestamps in output are replaced with 'timestamp'.
- // data: URIs for red.gif and green.gif
+ // data: URIs for red.gif, green.gif, circle.svg
$red = 'data:image/gif;base64,R0lGODlhAQABAIAAAP8AADAAACwAAAAAAQABAAACAkQBADs=';
$green = 'data:image/gif;base64,R0lGODlhAQABAIAAAACAADAAACwAAAAAAQABAAACAkQBADs=';
+ $svg = 'data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22UTF-8%22%3F%3E%0A'
+ . '%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%228%22%20height%3D'
+ . '%228%22%3E%0A%3Ccircle%20cx%3D%224%22%20cy%3D%224%22%20r%3D%222%22%2F%3E%0A%3C%2Fsvg%3E%0A';
return array(
array(
'foo { /* @embed */ background: url(large.png); }',
"foo { background: url(http://localhost/w/large.png?timestamp); }",
),
+ array(
+ 'SVG files are embedded without base64 encoding',
+ 'foo { /* @embed */ background: url(circle.svg); }',
+ "foo { background: url($svg); "
+ . "background: url(http://localhost/w/circle.svg?timestamp)!ie; }",
+ ),
array(
'Two regular files in one rule',
'foo { background: url(red.gif), url(green.gif); }',