mirror of
https://github.com/moodle/moodle.git
synced 2025-08-07 01:46:45 +02:00
MDL-55243 files: Make is_valid_image support SVG files
This commit is contained in:
parent
0e64d1d00d
commit
29f384fd91
7 changed files with 205 additions and 12 deletions
|
@ -2529,6 +2529,12 @@ function send_file($path, $filename, $lifetime = null , $filter=0, $pathisstring
|
|||
$filename = rawurlencode($filename);
|
||||
}
|
||||
|
||||
// We need to force download and force filter the file content for the SVG file.
|
||||
if (file_is_svg_image_from_mimetype($mimetype)) {
|
||||
$forcedownload = true;
|
||||
$filter = 1;
|
||||
}
|
||||
|
||||
if ($forcedownload) {
|
||||
header('Content-Disposition: attachment; filename="'.$filename.'"');
|
||||
|
||||
|
@ -2589,7 +2595,7 @@ function send_file($path, $filename, $lifetime = null , $filter=0, $pathisstring
|
|||
|
||||
} else {
|
||||
// Try to put the file through filters
|
||||
if ($mimetype == 'text/html' || $mimetype == 'application/xhtml+xml') {
|
||||
if ($mimetype == 'text/html' || $mimetype == 'application/xhtml+xml' || file_is_svg_image_from_mimetype($mimetype)) {
|
||||
$options = new stdClass();
|
||||
$options->noclean = true;
|
||||
$options->nocache = true; // temporary workaround for MDL-5136
|
||||
|
@ -3018,6 +3024,16 @@ function file_merge_draft_area_into_draft_area($getfromdraftid, $mergeintodrafti
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to determine whether the specified mime-type is an SVG image or not.
|
||||
*
|
||||
* @param string $mimetype Mime-type
|
||||
* @return bool True if it is an SVG file
|
||||
*/
|
||||
function file_is_svg_image_from_mimetype(string $mimetype): bool {
|
||||
return preg_match('|^image/svg|', $mimetype);
|
||||
}
|
||||
|
||||
/**
|
||||
* RESTful cURL class
|
||||
*
|
||||
|
|
|
@ -398,22 +398,61 @@ abstract class file_system {
|
|||
/**
|
||||
* Returns image information relating to the specified path or URL.
|
||||
*
|
||||
* @param string $path The path to pass to getimagesize.
|
||||
* @return array Containing width, height, and mimetype.
|
||||
* @param string $path The full path of the image file.
|
||||
* @return array|bool array that containing width, height, and mimetype or false if cannot get the image info.
|
||||
*/
|
||||
protected function get_imageinfo_from_path($path) {
|
||||
$imageinfo = getimagesize($path);
|
||||
$imagemimetype = file_storage::mimetype_from_file($path);
|
||||
$issvgimage = file_is_svg_image_from_mimetype($imagemimetype);
|
||||
|
||||
if (!is_array($imageinfo)) {
|
||||
return false; // Nothing to process, the file was not recognised as image by GD.
|
||||
if (!$issvgimage) {
|
||||
$imageinfo = getimagesize($path);
|
||||
if (!is_array($imageinfo)) {
|
||||
return false; // Nothing to process, the file was not recognised as image by GD.
|
||||
}
|
||||
$image = [
|
||||
'width' => $imageinfo[0],
|
||||
'height' => $imageinfo[1],
|
||||
'mimetype' => image_type_to_mime_type($imageinfo[2]),
|
||||
];
|
||||
} else {
|
||||
// Since SVG file is actually an XML file, GD cannot handle.
|
||||
$svgcontent = @simplexml_load_file($path);
|
||||
if (!$svgcontent) {
|
||||
// Cannot parse the file.
|
||||
return false;
|
||||
}
|
||||
$svgattrs = $svgcontent->attributes();
|
||||
|
||||
if (!empty($svgattrs->viewBox)) {
|
||||
// We have viewBox.
|
||||
$viewboxval = explode(' ', $svgattrs->viewBox);
|
||||
$width = intval($viewboxval[2]);
|
||||
$height = intval($viewboxval[3]);
|
||||
} else {
|
||||
// Get the width.
|
||||
if (!empty($svgattrs->width) && intval($svgattrs->width) > 0) {
|
||||
$width = intval($svgattrs->width);
|
||||
} else {
|
||||
// Default width.
|
||||
$width = 800;
|
||||
}
|
||||
// Get the height.
|
||||
if (!empty($svgattrs->height) && intval($svgattrs->height) > 0) {
|
||||
$height = intval($svgattrs->height);
|
||||
} else {
|
||||
// Default width.
|
||||
$height = 600;
|
||||
}
|
||||
}
|
||||
|
||||
$image = [
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'mimetype' => $imagemimetype,
|
||||
];
|
||||
}
|
||||
|
||||
$image = array(
|
||||
'width' => $imageinfo[0],
|
||||
'height' => $imageinfo[1],
|
||||
'mimetype' => image_type_to_mime_type($imageinfo[2]),
|
||||
);
|
||||
|
||||
if (empty($image['width']) or empty($image['height']) or empty($image['mimetype'])) {
|
||||
// GD can not parse it, sorry.
|
||||
return false;
|
||||
|
|
|
@ -940,6 +940,89 @@ class core_files_file_system_testcase extends advanced_testcase {
|
|||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that get_imageinfo_from_path returns an appropriate response
|
||||
* for an svg image with viewbox attribute.
|
||||
*/
|
||||
public function test_get_imageinfo_from_path_svg_viewbox() {
|
||||
$filepath = __DIR__ . '/fixtures/testimage_viewbox.svg';
|
||||
|
||||
// Get the filesystem mock.
|
||||
$fs = $this->get_testable_mock();
|
||||
|
||||
$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
|
||||
$method->setAccessible(true);
|
||||
$result = $method->invokeArgs($fs, [$filepath]);
|
||||
|
||||
$this->assertArrayHasKey('width', $result);
|
||||
$this->assertArrayHasKey('height', $result);
|
||||
$this->assertArrayHasKey('mimetype', $result);
|
||||
$this->assertEquals(100, $result['width']);
|
||||
$this->assertEquals(100, $result['height']);
|
||||
$this->assertStringContainsString('image/svg', $result['mimetype']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that get_imageinfo_from_path returns an appropriate response
|
||||
* for an svg image with width and height attributes.
|
||||
*/
|
||||
public function test_get_imageinfo_from_path_svg_with_width_height() {
|
||||
$filepath = __DIR__ . '/fixtures/testimage_width_height.svg';
|
||||
|
||||
// Get the filesystem mock.
|
||||
$fs = $this->get_testable_mock();
|
||||
|
||||
$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
|
||||
$method->setAccessible(true);
|
||||
$result = $method->invokeArgs($fs, [$filepath]);
|
||||
|
||||
$this->assertArrayHasKey('width', $result);
|
||||
$this->assertArrayHasKey('height', $result);
|
||||
$this->assertArrayHasKey('mimetype', $result);
|
||||
$this->assertEquals(100, $result['width']);
|
||||
$this->assertEquals(100, $result['height']);
|
||||
$this->assertStringContainsString('image/svg', $result['mimetype']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that get_imageinfo_from_path returns an appropriate response
|
||||
* for an svg image without attributes.
|
||||
*/
|
||||
public function test_get_imageinfo_from_path_svg_without_attribute() {
|
||||
$filepath = __DIR__ . '/fixtures/testimage.svg';
|
||||
|
||||
// Get the filesystem mock.
|
||||
$fs = $this->get_testable_mock();
|
||||
|
||||
$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
|
||||
$method->setAccessible(true);
|
||||
$result = $method->invokeArgs($fs, [$filepath]);
|
||||
|
||||
$this->assertArrayHasKey('width', $result);
|
||||
$this->assertArrayHasKey('height', $result);
|
||||
$this->assertArrayHasKey('mimetype', $result);
|
||||
$this->assertEquals(800, $result['width']);
|
||||
$this->assertEquals(600, $result['height']);
|
||||
$this->assertStringContainsString('image/svg', $result['mimetype']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that get_imageinfo_from_path returns an appropriate response
|
||||
* for a file which is not an correct svg.
|
||||
*/
|
||||
public function test_get_imageinfo_from_path_svg_invalid() {
|
||||
$filepath = __DIR__ . '/fixtures/testimage_error.svg';
|
||||
|
||||
// Get the filesystem mock.
|
||||
$fs = $this->get_testable_mock();
|
||||
|
||||
$method = new ReflectionMethod(file_system::class, 'get_imageinfo_from_path');
|
||||
$method->setAccessible(true);
|
||||
$result = $method->invokeArgs($fs, [$filepath]);
|
||||
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that get_content_file_handle returns a valid file handle.
|
||||
*
|
||||
|
|
14
lib/filestorage/tests/fixtures/testimage.svg
vendored
Normal file
14
lib/filestorage/tests/fixtures/testimage.svg
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id='gtop' stroke-width="12" stroke="#000">
|
||||
<g id="svgstar" transform="translate(50,50)">
|
||||
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
|
||||
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
|
||||
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
|
||||
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
|
||||
</g>
|
||||
</g>
|
||||
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 742 B |
13
lib/filestorage/tests/fixtures/testimage_error.svg
vendored
Normal file
13
lib/filestorage/tests/fixtures/testimage_error.svg
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id='gtop' stroke-width="12" stroke="#000">
|
||||
<g id="svgstar" transform="translate(50,50)">
|
||||
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
|
||||
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
|
||||
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
|
||||
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
|
||||
</g>
|
||||
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 729 B |
14
lib/filestorage/tests/fixtures/testimage_viewbox.svg
vendored
Normal file
14
lib/filestorage/tests/fixtures/testimage_viewbox.svg
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
|
||||
<g id='gtop' stroke-width="12" stroke="#000">
|
||||
<g id="svgstar" transform="translate(50,50)">
|
||||
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
|
||||
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
|
||||
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
|
||||
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
|
||||
</g>
|
||||
</g>
|
||||
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 764 B |
14
lib/filestorage/tests/fixtures/testimage_width_height.svg
vendored
Normal file
14
lib/filestorage/tests/fixtures/testimage_width_height.svg
vendored
Normal file
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
|
||||
<!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">
|
||||
]>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100px" height="100px">
|
||||
<g id='gtop' stroke-width="12" stroke="#000">
|
||||
<g id="svgstar" transform="translate(50,50)">
|
||||
<path id="svgbar" d="M-27-5a7,7,0,1,0,0,10h54a7,7,0,1,0,0-10z"/>
|
||||
<use id='use1' xlink:href="#svgbar" transform="rotate(45)"/>
|
||||
<use id='use2' xlink:href="#svgbar" transform="rotate(90)"/>
|
||||
<use id='use3' xlink:href="#svgbar" transform="rotate(135)"/>
|
||||
</g>
|
||||
</g>
|
||||
<use id="usetop" xlink:href="#svgstar" fill="#FB4"/>
|
||||
</svg>
|
After Width: | Height: | Size: 771 B |
Loading…
Add table
Add a link
Reference in a new issue