Skip to content

Commit f36dd0a

Browse files
author
kobo-bot[bot]
committed
Merge branch 'release/2.025.43' into release/2.025.47
2 parents d861759 + 4ee8d57 commit f36dd0a

File tree

8 files changed

+61
-5
lines changed

8 files changed

+61
-5
lines changed

dependencies/pip/dev_requirements.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,11 @@ pathspec==0.12.1
462462
pexpect==4.9.0
463463
# via ipython
464464
pillow==10.3.0
465-
# via django-markdownx
465+
# via
466+
# django-markdownx
467+
# pillow-heif
468+
pillow-heif==0.22.0
469+
# via -r dependencies/pip/requirements.in
466470
platformdirs==4.3.2
467471
# via black
468472
pluggy==1.4.0

dependencies/pip/requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ oauthlib
7575
objgraph
7676
openpyxl
7777
#py-gfm # Incompatible with markdown 3.x
78+
pillow-heif
7879
psycopg
7980
pymongo
8081
python-dateutil

dependencies/pip/requirements.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,11 @@ openpyxl==3.1.3
369369
pandas==2.0.3
370370
# via -r dependencies/pip/requirements.in
371371
pillow==10.3.0
372-
# via django-markdownx
372+
# via
373+
# django-markdownx
374+
# pillow-heif
375+
pillow-heif==0.22.0
376+
# via -r dependencies/pip/requirements.in
373377
prometheus-client==0.20.0
374378
# via
375379
# django-prometheus
Binary file not shown.

kobo/apps/openrosa/apps/logger/tests/models/test_attachment.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,33 @@ def test_thumbnails(self):
5555
thumbnail = f'{filename}-{size}.jpg'
5656
self.assertFalse(default_storage.exists(thumbnail))
5757

58+
def test_heic_named_file_generates_jpg_thumbnails(self):
59+
media_file_name = 'test_image.heic'
60+
media_file = os.path.join(
61+
os.path.dirname(os.path.dirname(__file__)),
62+
'fixtures',
63+
'images',
64+
media_file_name
65+
)
66+
with open(media_file, 'rb') as f:
67+
attachment = Attachment.objects.create(
68+
instance=self.instance,
69+
media_file=ContentFile(f.read(), name=media_file_name),
70+
)
71+
72+
image_url(attachment, 'small')
73+
for size in ['small', 'medium', 'large']:
74+
filename = attachment.media_file.name.replace('.heic', '')
75+
thumbnail = f'{filename}-{size}.jpg'
76+
self.assertTrue(default_storage.exists(thumbnail))
77+
78+
# Ensure clean up of thumbnails
79+
attachment.delete()
80+
for size in ['small', 'medium', 'large']:
81+
filename = attachment.media_file.name.replace('.heic', '')
82+
thumbnail = f'{filename}-{size}.jpg'
83+
self.assertFalse(default_storage.exists(thumbnail))
84+
5885
def test_create_thumbnails_command(self):
5986
call_command('create_image_thumbnails')
6087
created_times = {}

kobo/apps/openrosa/libs/utils/image_tools.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
# coding: utf-8
22
from io import BytesIO
3+
from mimetypes import guess_type
34
from tempfile import NamedTemporaryFile
45

56
import requests
67
from django.conf import settings
78
from django.core.files.base import ContentFile
89
from django.core.files.storage import FileSystemStorage
910
from PIL import Image
11+
from pillow_heif import register_heif_opener
1012

1113
from kobo.apps.openrosa.libs.utils.viewer_tools import get_optimized_image_path
14+
from kpi.constants import UNSUPPORTED_INLINE_MIMETYPES
1215
from kpi.deployment_backends.kc_access.storage import (
1316
default_kobocat_storage as default_storage,
1417
)
@@ -39,9 +42,16 @@ def get_dimensions(size_, longest_side):
3942

4043

4144
def _save_thumbnails(image, original_path, size, suffix):
45+
img_format = image.format
46+
47+
# Change format to JPEG for unsupported inline mimetypes
48+
mimetype, _ = guess_type(original_path)
49+
if mimetype in UNSUPPORTED_INLINE_MIMETYPES:
50+
img_format = 'JPEG'
51+
4252
# Thumbnail format will be set by original file extension.
4353
# Use same format to keep transparency of GIF/PNG
44-
nm = NamedTemporaryFile(suffix='.%s' % image.format)
54+
nm = NamedTemporaryFile(suffix='.%s' % img_format)
4555
try:
4656
# Ensure conversion to float in operations
4757
image.thumbnail(get_dimensions(image.size, float(size)), Image.LANCZOS)
@@ -71,6 +81,7 @@ def _save_thumbnails(image, original_path, size, suffix):
7181

7282
def resize(filename):
7383
image = None
84+
register_heif_opener()
7485
if isinstance(default_storage, FileSystemStorage):
7586
path = default_storage.path(filename)
7687
image = Image.open(path)

kobo/apps/openrosa/libs/utils/viewer_tools.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import traceback
44
import zipfile
55
from datetime import datetime
6+
from mimetypes import guess_type
67
from tempfile import NamedTemporaryFile
78

89
import requests
@@ -13,6 +14,7 @@
1314
from django.utils.translation import gettext as t
1415
from ua_parser import user_agent_parser as ua_parse
1516

17+
from kpi.constants import UNSUPPORTED_INLINE_MIMETYPES
1618
from kpi.deployment_backends.kc_access.storage import (
1719
default_kobocat_storage as default_storage,
1820
)
@@ -46,6 +48,9 @@ def get_mongo_userform_id(xform: 'logger.XForm', username: str = None) -> str:
4648

4749
def get_optimized_image_path(path: str, suffix: str) -> str:
4850
file_name, ext = os.path.splitext(path)
51+
mimetype, _ = guess_type(path)
52+
if mimetype in UNSUPPORTED_INLINE_MIMETYPES:
53+
ext = '.jpg'
4954
return f'{file_name}-{suffix}{ext}'
5055

5156

kpi/constants.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,6 @@
169169
'image/jp2',
170170
'image/x-icon',
171171
'image/vnd.wap.wbmp',
172-
'image/heic',
173-
'image/heif',
174172
# audio
175173
'audio/mpeg',
176174
'audio/mp3',
@@ -200,3 +198,9 @@
200198
'video/x-msvideo',
201199
'video/x-ms-wmv',
202200
}
201+
202+
# MIME types not supported inline by browsers, fallback to JPEG conversion
203+
UNSUPPORTED_INLINE_MIMETYPES = [
204+
'image/heic',
205+
'image/heif',
206+
]

0 commit comments

Comments
 (0)