Skip to content

Commit

Permalink
Merge pull request #1150 from Automattic/add/svg-image-dimension-extr…
Browse files Browse the repository at this point in the history
…action

Add support for extracting (pixel) dimensions from SVG images
  • Loading branch information
westonruter authored May 18, 2018
2 parents 9703ddf + 53c2a2a commit adc8fed
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 16 deletions.
2 changes: 1 addition & 1 deletion includes/lib/fasterimage/FasterImage.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ protected function handle($url, & $result)
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_BUFFERSIZE, 256);
curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
Expand Down
45 changes: 43 additions & 2 deletions includes/lib/fasterimage/ImageParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public function parseSize()
return $this->parseSizeForPSD();
case 'webp':
return $this->parseSizeForWebp();
case 'svg':
return $this->parseSizeForSvg();
}

return null;
Expand Down Expand Up @@ -133,7 +135,13 @@ public function parseType()
case "MM":
return $this->type = 'tiff';
default:
return false;
$this->stream->resetPointer();
$markup = $this->stream->read( 1024 );
if ( false !== strpos( $markup, '<svg' ) ) {
$this->type = 'svg';
} else {
return false;
}
}
}

Expand Down Expand Up @@ -345,6 +353,39 @@ protected function parseSizeForWebp()

}

/**
* Parse size for SVG.
*
* @return array|null Size or null.
*/
protected function parseSizeForSvg()
{
$this->stream->resetPointer();
$markup = $this->stream->read( 1024 );
if ( ! preg_match( '#<svg.*?>#s', $markup, $matches ) ) {
return null;
}
$svg_start_tag = $matches[0];
$width = null;
$height = null;
if ( preg_match( '/\swidth=([\'"])(\d+(\.\d+)?)(px)?\1/', $svg_start_tag, $matches ) ) {
$width = floatval( $matches[2] );
}
if ( preg_match( '/\sheight=([\'"])(\d+(\.\d+)?)(px)?\1/', $svg_start_tag, $matches ) ) {
$height = floatval( $matches[2] );
}
if ( $width && $height ) {
return [ $width, $height ];
}
if ( preg_match( '/\sviewBox=([\'"])[^\1]*(?:,|\s)+(?P<width>\d+(?:\.\d+)?)(?:px)?(?:,|\s)+(?P<height>\d+(?:\.\d+)?)(?:px)?\s*\1/', $svg_start_tag, $matches ) ) {
return [
floatval( $matches['width'] ),
floatval( $matches['height'] )
];
}
return null;
}

/**
* @return mixed
*/
Expand Down Expand Up @@ -374,4 +415,4 @@ private function readInt($str)

return ($size[1] << 8) + $size[2];
}
}
}
42 changes: 40 additions & 2 deletions includes/lib/fastimage/class-fastimage.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,14 @@ public function getType()
case chr(0x89).'P':
return $this->type = 'png';
default:
return false;
$this->strpos = 0;
$markup = $this->getChars( 1024 );
$this->strpos = 0;
if ( false !== strpos( $markup, '<svg' ) ) {
$this->type = 'svg';
} else {
return false;
}
}
}
return $this->type;
Expand All @@ -88,6 +95,8 @@ private function parseSize()
return $this->parseSizeForBMP();
case 'jpeg':
return $this->parseSizeForJPEG();
case 'svg':
return $this->parseSizeForSVG();
}
return null;
}
Expand Down Expand Up @@ -158,6 +167,35 @@ private function parseSizeForJPEG()
}
}
}

private function parseSizeForSVG()
{
$this->strpos = 0;
$markup = $this->getChars( 1024 );
if ( ! preg_match( '#<svg.*?>#s', $markup, $matches ) ) {
return null;
}
$svg_start_tag = $matches[0];
$width = null;
$height = null;
if ( preg_match( '/\swidth=([\'"])(\d+(\.\d+)?)(px)?\1/', $svg_start_tag, $matches ) ) {
$width = floatval( $matches[2] );
}
if ( preg_match( '/\sheight=([\'"])(\d+(\.\d+)?)(px)?\1/', $svg_start_tag, $matches ) ) {
$height = floatval( $matches[2] );
}
if ( $width && $height ) {
return array( $width, $height );
}
if ( preg_match( '/\sviewBox=([\'"])[^\1]*(?:,|\s)+(?P<width>\d+(?:\.\d+)?)(?:px)?(?:,|\s)+(?P<height>\d+(?:\.\d+)?)(?:px)?\s*\1/', $svg_start_tag, $matches ) ) {
return array(
floatval( $matches['width'] ),
floatval( $matches['height'] )
);
}
return null;
}

private function getChars($n)
{
$response = null;
Expand Down Expand Up @@ -198,4 +236,4 @@ public function __destruct()
{
$this->close();
}
}
}
43 changes: 32 additions & 11 deletions tests/test-amp-image-dimension-extractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
* @package AMP
*/

define( 'AMP_IMG_DIMENSION_TEST_VALID_FILE', dirname( __FILE__ ) . '/assets/wordpress-logo.png' );
define( 'AMP_IMG_DIMENSION_TEST_INVALID_FILE', dirname( __FILE__ ) . '/assets/not-exists.png' );

// Not ideal to use remote URLs (since the remote service can change); mocking would be better.
define( 'IMG_350', 'http://i0.wp.com/amptest.files.wordpress.com/2017/03/350x150.png' );
define( 'IMG_1024', 'http://i0.wp.com/amptest.files.wordpress.com/2017/03/1024x768.png' );
define( 'IMG_SVG', 'https://gist.githubusercontent.com/westonruter/90fbaaced3851bf6ef762996c8c4375d/raw/fd58ec3fc426645885f6a3afa58ad64fbc70ea89/amp.svg' ); // @todo For some reason, FasterImage times out on this if the XML PI is absent.
define( 'IMG_SVG_VIEWPORT', 'https://gist.githubusercontent.com/westonruter/90fbaaced3851bf6ef762996c8c4375d/raw/fd58ec3fc426645885f6a3afa58ad64fbc70ea89/google.svg' );

/**
* Tests for AMP_Image_Dimension_Extractor.
Expand Down Expand Up @@ -157,18 +158,28 @@ public function test__valid_image_file_synchronous() {
*/
public function test__multiple_valid_image_files() {
$sources = array(
IMG_350 => false,
IMG_1024 => false,
IMG_350 => false,
IMG_1024 => false,
IMG_SVG => false,
IMG_SVG_VIEWPORT => false,
);
$expected = array(
IMG_350 => array(
IMG_350 => array(
'width' => 350,
'height' => 150,
),
IMG_1024 => array(
IMG_1024 => array(
'width' => 1024,
'height' => 768,
),
IMG_SVG => array(
'width' => 175,
'height' => 60,
),
IMG_SVG_VIEWPORT => array(
'width' => 251,
'height' => 80,
),
);

$dimensions = AMP_Image_Dimension_Extractor::extract_by_downloading_images( $sources );
Expand All @@ -181,18 +192,28 @@ public function test__multiple_valid_image_files() {
*/
public function test__multiple_valid_image_files_synchronous() {
$sources = array(
IMG_350 => false,
IMG_1024 => false,
IMG_350 => false,
IMG_1024 => false,
IMG_SVG => false,
IMG_SVG_VIEWPORT => false,
);
$expected = array(
IMG_350 => array(
IMG_350 => array(
'width' => 350,
'height' => 150,
),
IMG_1024 => array(
IMG_1024 => array(
'width' => 1024,
'height' => 768,
),
IMG_SVG => array(
'width' => 175,
'height' => 60,
),
IMG_SVG_VIEWPORT => array(
'width' => 251,
'height' => 80,
),
);

$dimensions = AMP_Image_Dimension_Extractor::extract_by_downloading_images( $sources, 'synchronous' );
Expand Down Expand Up @@ -287,11 +308,11 @@ public function test__mix_of_valid_and_invalid_image_file_synchronous() {
/**
* Test get_default_user_agent()
*
* @covers get_default_user_agent()
* @covers \AMP_Image_Dimension_Extractor::get_default_user_agent()
*/
public function test__amp_wp_user_agent() {
$expected = 'amp-wp, v' . AMP__VERSION . ', ';
$user_agent = AMP_Image_Dimension_Extractor::get_default_user_agent( '' );
$user_agent = AMP_Image_Dimension_Extractor::get_default_user_agent();
$user_agent = substr( $user_agent, 0, strlen( $expected ) );

$this->assertEquals( $expected, $user_agent );
Expand Down

0 comments on commit adc8fed

Please sign in to comment.