]> BookStack Code Mirror - bookstack/commitdiff
Added SVG support to the image gallery.
authorDan Brown <redacted>
Sun, 22 May 2022 10:52:42 +0000 (11:52 +0100)
committerDan Brown <redacted>
Sun, 22 May 2022 10:52:42 +0000 (11:52 +0100)
app/Http/Controllers/Controller.php
app/Uploads/ImageService.php
tests/Uploads/ImageTest.php
tests/Uploads/UsesImages.php
tests/test-data/diagram.svg [new file with mode: 0644]

index 5b2221fc1235c412ae00c57971d77425ec8b737c..01911808f47dd8981c55c5565f684eda0a80dd9d 100644 (file)
@@ -219,6 +219,6 @@ abstract class Controller extends BaseController
      */
     protected function getImageValidationRules(): array
     {
-        return ['image_extension', 'mimes:jpeg,png,gif,webp', 'max:' . (config('app.upload_limit') * 1000)];
+        return ['image_extension', 'mimes:jpeg,png,gif,webp,svg', 'max:' . (config('app.upload_limit') * 1000)];
     }
 }
index ca0db997b47a77b727ceb7eb33c126b49a62a0a5..b5e048892a2e510787ef544e32fab399d7a1eb28 100644 (file)
@@ -30,7 +30,7 @@ class ImageService
     protected $image;
     protected $fileSystem;
 
-    protected static $supportedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
+    protected static $supportedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'];
 
     /**
      * ImageService constructor.
@@ -230,6 +230,14 @@ class ImageService
         return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'gif';
     }
 
+    /**
+     * Check if the given image is an SVG image file.
+     */
+    protected function isSvg(Image $image): bool
+    {
+        return strtolower(pathinfo($image->path, PATHINFO_EXTENSION)) === 'svg';
+    }
+
     /**
      * Check if the given image and image data is apng.
      */
@@ -255,8 +263,8 @@ class ImageService
      */
     public function getThumbnail(Image $image, ?int $width, ?int $height, bool $keepRatio = false): string
     {
-        // Do not resize GIF images where we're not cropping
-        if ($keepRatio && $this->isGif($image)) {
+        // Do not resize GIF images where we're not cropping or SVG images.
+        if (($keepRatio && $this->isGif($image)) || $this->isSvg($image)) {
             return $this->getPublicUrl($image->path);
         }
 
index 01754d2dec13c4dba843501723b3f9c0c1a9fcf1..4188968289a26fe0cd67685af0b629c9e1191746 100644 (file)
@@ -74,6 +74,23 @@ class ImageTest extends TestCase
         $this->assertStringNotContainsString('thumbs-', $imgDetails['response']->thumbs->display);
     }
 
+    public function test_svg_upload()
+    {
+        /** @var Page $page */
+        $page = Page::query()->first();
+        $admin = $this->getAdmin();
+        $this->actingAs($admin);
+
+        $imgDetails = $this->uploadGalleryImage($page, 'diagram.svg', 'image/svg+xml');
+        $this->assertFileExists(public_path($imgDetails['path']));
+        $this->assertTrue(
+            $imgDetails['response']->url === $imgDetails['response']->thumbs->gallery
+            && $imgDetails['response']->url === $imgDetails['response']->thumbs->display,
+        );
+
+        $this->deleteImage($imgDetails['path']);
+    }
+
     public function test_image_edit()
     {
         $editor = $this->getEditor();
index b55572248a8bc1668da8b34cce264b696c5fa9b7..513a5d49e9a5430b058606c2df173b5b2bbdcadb 100644 (file)
@@ -3,6 +3,7 @@
 namespace Tests\Uploads;
 
 use BookStack\Entities\Models\Page;
+use BookStack\Uploads\Image;
 use Illuminate\Http\UploadedFile;
 use stdClass;
 
@@ -84,21 +85,19 @@ trait UsesImages
      * Returns the image name.
      * Can provide a page to relate the image to.
      *
-     * @param Page|null $page
-     *
      * @return array{name: string, path: string, page: Page, response: stdClass}
      */
-    protected function uploadGalleryImage(Page $page = null, ?string $testDataFileName = null)
+    protected function uploadGalleryImage(Page $page = null, string $testDataFileName = 'first-image.png', string $contentType = 'image/png')
     {
         if ($page === null) {
             $page = Page::query()->first();
         }
 
-        $imageName = $testDataFileName ?? 'first-image.png';
+        $imageName = $testDataFileName;
         $relPath = $this->getTestImagePath('gallery', $imageName);
         $this->deleteImage($relPath);
 
-        $upload = $this->uploadImage($imageName, $page->id, 'image/png', $testDataFileName);
+        $upload = $this->uploadImage($imageName, $page->id, $contentType, $testDataFileName);
         $upload->assertStatus(200);
 
         return [
diff --git a/tests/test-data/diagram.svg b/tests/test-data/diagram.svg
new file mode 100644 (file)
index 0000000..75e3a49
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" style="background-color: rgb(255, 255, 255);" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="121px" height="141px" viewBox="-0.5 -0.5 121 141"><defs/><g><ellipse cx="25" cy="87.5" rx="7.5" ry="7.5" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/><path d="M 25 95 L 25 120 M 25 100 L 10 100 M 25 100 L 40 100 M 25 120 L 10 140 M 25 120 L 40 140" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><path d="M 0 0 L 120 0 L 120 50 L 80 50 L 60 80 L 60 50 L 0 50 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 25px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0px; text-align: center;" data-drawio-colors="color: rgb(0, 0, 0); "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Hello!</div></div></div></foreignObject><text x="60" y="29" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">Hello!</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg>
\ No newline at end of file