Repository: oppia/oppia

Issue:
Long chapter name overflows the thumbnail
**Describe the bug**
A 36 characters long story chapter name overflows the thumbnail image.

**To Reproduce**
Steps to reproduce the behavior:
 1. Go to 'Story Editor'
 2. Create a chapter with 36 characters long name
 3. Click on the add thumbnail
 4. Upload SVG image
 5. Click on 'Add Thumbnail'

**Observed behavior**
The chapter name overflows the background.

**Expected behavior**
The chapter name to be ellipsized. (Chapter…)

**Screenshots**
![image](https://user-images.githubusercontent.com/10142938/80872665-9c726a00-8cb3-11ea-8acd-1cdb68214eae.png)

**Desktop (please complete the following information; delete this section if the issue does not arise on desktop):**
 - OS: Ubuntu 18
 - Browser: Firefox
 - Version: 75



Discussion:
The same applies to the story thumbnail
![image](https://user-images.githubusercontent.com/10142938/80872813-ab0d5100-8cb4-11ea-9fa6-e3d0f1f1246c.png)

And topic
![image](https://user-images.githubusercontent.com/10142938/80872848-e14ad080-8cb4-11ea-8495-e974bf6bd9a1.png)


Resolving patch:
diff --git a/core/controllers/editor.py b/core/controllers/editor.py
--- a/core/controllers/editor.py
+++ b/core/controllers/editor.py
@@ -690,9 +690,11 @@ def post(self, entity_type, entity_id):
             raise self.InvalidInputException(
                 'A file with the name %s already exists. Please choose a '
                 'different name.' % filename)
-
+        image_is_compressible = (
+            file_format in feconf.COMPRESSIBLE_IMAGE_FORMATS)
         fs_services.save_original_and_compressed_versions_of_image(
-            filename, entity_type, entity_id, raw, filename_prefix)
+            filename, entity_type, entity_id, raw, filename_prefix,
+            image_is_compressible)
 
         self.render_json({'filename': filename})
 
diff --git a/core/domain/fs_services.py b/core/domain/fs_services.py
--- a/core/domain/fs_services.py
+++ b/core/domain/fs_services.py
@@ -30,7 +30,7 @@
 
 def save_original_and_compressed_versions_of_image(
         filename, entity_type, entity_id, original_image_content,
-        filename_prefix):
+        filename_prefix, image_is_compressible):
     """Saves the three versions of the image file.
 
     Args:
@@ -39,6 +39,8 @@ def save_original_and_compressed_versions_of_image(
         entity_id: str. The id of the entity.
         original_image_content: str. The content of the original image.
         filename_prefix: str. The string to prefix to the filename.
+        image_is_compressible: bool. Whether the image can be compressed or
+            not.
     """
     filepath = '%s/%s' % (filename_prefix, filename)
 
@@ -58,10 +60,14 @@ def save_original_and_compressed_versions_of_image(
     fs = fs_domain.AbstractFileSystem(file_system_class(
         entity_type, entity_id))
 
-    compressed_image_content = gae_image_services.compress_image(
-        original_image_content, 0.8)
-    micro_image_content = gae_image_services.compress_image(
-        original_image_content, 0.7)
+    if image_is_compressible:
+        compressed_image_content = gae_image_services.compress_image(
+            original_image_content, 0.8)
+        micro_image_content = gae_image_services.compress_image(
+            original_image_content, 0.7)
+    else:
+        compressed_image_content = original_image_content
+        micro_image_content = original_image_content
 
     # Because in case of CreateVersionsOfImageJob, the original image is
     # already there. Also, even if the compressed, micro versions for some
diff --git a/feconf.py b/feconf.py
--- a/feconf.py
+++ b/feconf.py
@@ -305,16 +305,25 @@
 # Shared secret key for default VM.
 DEFAULT_VM_SHARED_SECRET = '1a2b3c4e'
 
+IMAGE_FORMAT_JPEG = 'jpeg'
+IMAGE_FORMAT_PNG = 'png'
+IMAGE_FORMAT_GIF = 'gif'
+IMAGE_FORMAT_SVG = 'svg'
+
 # An array containing the accepted image formats (as determined by the imghdr
 # module) and the corresponding allowed extensions in the filenames of uploaded
 # images.
 ACCEPTED_IMAGE_FORMATS_AND_EXTENSIONS = {
-    'jpeg': ['jpg', 'jpeg'],
-    'png': ['png'],
-    'gif': ['gif'],
-    'svg': ['svg']
+    IMAGE_FORMAT_JPEG: ['jpg', 'jpeg'],
+    IMAGE_FORMAT_PNG: ['png'],
+    IMAGE_FORMAT_GIF: ['gif'],
+    IMAGE_FORMAT_SVG: ['svg']
 }
 
+# An array containing the image formats that can be compressed.
+COMPRESSIBLE_IMAGE_FORMATS = [
+    IMAGE_FORMAT_JPEG, IMAGE_FORMAT_PNG, IMAGE_FORMAT_GIF]
+
 # An array containing the accepted audio extensions for uploaded files and
 # the corresponding MIME types.
 ACCEPTED_AUDIO_EXTENSIONS = {


Test patch:
diff --git a/core/domain/fs_services_test.py b/core/domain/fs_services_test.py
--- a/core/domain/fs_services_test.py
+++ b/core/domain/fs_services_test.py
@@ -80,7 +80,7 @@ def test_save_original_and_compressed_versions_of_image(self):
             fs.isfile('image/%s' % self.MICRO_IMAGE_FILENAME), False)
         fs_services.save_original_and_compressed_versions_of_image(
             self.FILENAME, 'exploration', self.EXPLORATION_ID,
-            original_image_content, 'image')
+            original_image_content, 'image', True)
         self.assertEqual(fs.isfile('image/%s' % self.FILENAME), True)
         self.assertEqual(
             fs.isfile('image/%s' % self.COMPRESSED_IMAGE_FILENAME), True)
@@ -112,7 +112,7 @@ def test_compress_image_on_prod_mode_with_big_image_size(self):
 
             fs_services.save_original_and_compressed_versions_of_image(
                 self.FILENAME, 'exploration', self.EXPLORATION_ID,
-                original_image_content, 'image')
+                original_image_content, 'image', True)
 
             self.assertTrue(fs.isfile('image/%s' % self.FILENAME))
             self.assertTrue(
@@ -157,7 +157,7 @@ def test_compress_image_on_prod_mode_with_small_image_size(self):
 
             fs_services.save_original_and_compressed_versions_of_image(
                 self.FILENAME, 'exploration', self.EXPLORATION_ID,
-                original_image_content, 'image')
+                original_image_content, 'image', True)
 
             self.assertTrue(fs.isfile('image/%s' % self.FILENAME))
             self.assertTrue(
@@ -184,6 +184,42 @@ def test_compress_image_on_prod_mode_with_small_image_size(self):
                     micro_image_content),
                 (22, 22))
 
+    def test_save_original_and_compressed_versions_of_svg_image(self):
+        with python_utils.open_file(
+            os.path.join(feconf.TESTS_DATA_DIR, 'test_svg.svg'), mode='rb',
+            encoding=None) as f:
+            image_content = f.read()
+
+        with self.swap(constants, 'DEV_MODE', False):
+            fs = fs_domain.AbstractFileSystem(
+                fs_domain.GcsFileSystem(
+                    feconf.ENTITY_TYPE_EXPLORATION, self.EXPLORATION_ID))
+
+            self.assertFalse(fs.isfile('image/%s' % self.FILENAME))
+            self.assertFalse(
+                fs.isfile('image/%s' % self.COMPRESSED_IMAGE_FILENAME))
+            self.assertFalse(fs.isfile('image/%s' % self.MICRO_IMAGE_FILENAME))
+
+            fs_services.save_original_and_compressed_versions_of_image(
+                self.FILENAME, 'exploration', self.EXPLORATION_ID,
+                image_content, 'image', False)
+
+            self.assertTrue(fs.isfile('image/%s' % self.FILENAME))
+            self.assertTrue(
+                fs.isfile('image/%s' % self.COMPRESSED_IMAGE_FILENAME))
+            self.assertTrue(fs.isfile('image/%s' % self.MICRO_IMAGE_FILENAME))
+
+            original_image_content = fs.get(
+                'image/%s' % self.FILENAME)
+            compressed_image_content = fs.get(
+                'image/%s' % self.COMPRESSED_IMAGE_FILENAME)
+            micro_image_content = fs.get(
+                'image/%s' % self.MICRO_IMAGE_FILENAME)
+
+            self.assertEqual(original_image_content, image_content)
+            self.assertEqual(compressed_image_content, image_content)
+            self.assertEqual(micro_image_content, image_content)
+
 
 class FileSystemClassifierDataTests(test_utils.GenericTestBase):
     """Unit tests for storing, reading and deleting classifier data."""