misc: add |convert_image_format filter tag (#86003)
gitea/wcs/pipeline/head This commit looks good
Details
gitea/wcs/pipeline/head This commit looks good
Details
This commit is contained in:
parent
16d1e680d0
commit
445dac2e9b
|
@ -43,7 +43,8 @@ Depends: graphviz,
|
||||||
uwsgi-plugin-python3,
|
uwsgi-plugin-python3,
|
||||||
${misc:Depends},
|
${misc:Depends},
|
||||||
${python3:Depends},
|
${python3:Depends},
|
||||||
Recommends: libreoffice-writer-nogui | libreoffice-writer,
|
Recommends: graphicsmagick,
|
||||||
|
libreoffice-writer-nogui | libreoffice-writer,
|
||||||
poppler-utils,
|
poppler-utils,
|
||||||
python3-docutils,
|
python3-docutils,
|
||||||
python3-langdetect,
|
python3-langdetect,
|
||||||
|
|
|
@ -2,6 +2,8 @@ import datetime
|
||||||
import html
|
import html
|
||||||
import os
|
import os
|
||||||
import string
|
import string
|
||||||
|
import subprocess
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
@ -1696,3 +1698,83 @@ def test_details_format(pub):
|
||||||
pub.loggederror_class.wipe()
|
pub.loggederror_class.wipe()
|
||||||
assert tmpl.render(context) == 'String:\n foo'
|
assert tmpl.render(context) == 'String:\n foo'
|
||||||
assert pub.loggederror_class.count() == 0
|
assert pub.loggederror_class.count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('image_format', ['jpeg', 'png', 'pdf'])
|
||||||
|
def test_convert_image_format(pub, image_format):
|
||||||
|
with pub.complex_data():
|
||||||
|
img = Template('{{ url|qrcode|convert_image_format:"%s" }}' % image_format).render(
|
||||||
|
{'url': 'http://example.com/', 'allow_complex': True}
|
||||||
|
)
|
||||||
|
assert pub.has_cached_complex_data(img)
|
||||||
|
value = pub.get_cached_complex_data(img)
|
||||||
|
assert value.orig_filename == 'qrcode.%s' % image_format
|
||||||
|
assert value.content_type == {'jpeg': 'image/jpeg', 'png': 'image/png', 'pdf': 'application/pdf'}.get(
|
||||||
|
image_format
|
||||||
|
)
|
||||||
|
with value.get_file_pointer() as fp:
|
||||||
|
if image_format in ('jpeg', 'png'):
|
||||||
|
img = PIL.Image.open(fp)
|
||||||
|
assert img.format == image_format.upper()
|
||||||
|
assert img.size == (330, 330)
|
||||||
|
assert (
|
||||||
|
zbar_decode_qrcode(img, symbols=[ZBarSymbol.QRCODE])[0].data.decode()
|
||||||
|
== 'http://example.com/'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
assert b'%PDF-' in fp.read()[:200]
|
||||||
|
|
||||||
|
|
||||||
|
def test_convert_image_format_no_name(pub):
|
||||||
|
with pub.complex_data():
|
||||||
|
img = Template('{{ url|qrcode|rename_file:""|convert_image_format:"jpeg" }}').render(
|
||||||
|
{'url': 'http://example.com/', 'allow_complex': True}
|
||||||
|
)
|
||||||
|
assert pub.has_cached_complex_data(img)
|
||||||
|
value = pub.get_cached_complex_data(img)
|
||||||
|
assert value.orig_filename == 'file.jpeg'
|
||||||
|
|
||||||
|
|
||||||
|
def test_convert_image_format_errors(pub):
|
||||||
|
pub.loggederror_class.wipe()
|
||||||
|
with pub.complex_data():
|
||||||
|
img = Template('{{ "xxx"|convert_image_format:"gif" }}').render({'allow_complex': True})
|
||||||
|
assert pub.has_cached_complex_data(img)
|
||||||
|
assert pub.get_cached_complex_data(img) is None
|
||||||
|
assert pub.loggederror_class.count() == 1
|
||||||
|
assert (
|
||||||
|
pub.loggederror_class.select()[0].summary
|
||||||
|
== '|convert_image_format: unknown format (must be one of jpeg, pdf, png)'
|
||||||
|
)
|
||||||
|
|
||||||
|
pub.loggederror_class.wipe()
|
||||||
|
with pub.complex_data():
|
||||||
|
img = Template('{{ "xxx"|convert_image_format:"jpeg" }}').render({'allow_complex': True})
|
||||||
|
assert pub.has_cached_complex_data(img)
|
||||||
|
assert pub.get_cached_complex_data(img) is None
|
||||||
|
assert pub.loggederror_class.count() == 1
|
||||||
|
assert pub.loggederror_class.select()[0].summary == '|convert_image_format: missing input'
|
||||||
|
|
||||||
|
pub.loggederror_class.wipe()
|
||||||
|
with mock.patch('subprocess.run', side_effect=FileNotFoundError()):
|
||||||
|
with pub.complex_data():
|
||||||
|
img = Template('{{ url|qrcode|convert_image_format:"jpeg" }}').render(
|
||||||
|
{'url': 'http://example.com/', 'allow_complex': True}
|
||||||
|
)
|
||||||
|
assert pub.has_cached_complex_data(img)
|
||||||
|
assert pub.get_cached_complex_data(img) is None
|
||||||
|
assert pub.loggederror_class.count() == 1
|
||||||
|
assert pub.loggederror_class.select()[0].summary == '|convert_image_format: not supported'
|
||||||
|
|
||||||
|
pub.loggederror_class.wipe()
|
||||||
|
with mock.patch(
|
||||||
|
'subprocess.run', side_effect=subprocess.CalledProcessError(returncode=-1, cmd='xx', stderr=b'xxx')
|
||||||
|
):
|
||||||
|
with pub.complex_data():
|
||||||
|
img = Template('{{ url|qrcode|convert_image_format:"jpeg" }}').render(
|
||||||
|
{'url': 'http://example.com/', 'allow_complex': True}
|
||||||
|
)
|
||||||
|
assert pub.has_cached_complex_data(img)
|
||||||
|
assert pub.get_cached_complex_data(img) is None
|
||||||
|
assert pub.loggederror_class.count() == 1
|
||||||
|
assert pub.loggederror_class.select()[0].summary == '|convert_image_format: conversion error (xxx)'
|
||||||
|
|
|
@ -24,6 +24,7 @@ import math
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
import subprocess
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
from decimal import DivisionByZero as DecimalDivisionByZero
|
from decimal import DivisionByZero as DecimalDivisionByZero
|
||||||
|
@ -1086,6 +1087,58 @@ def rename_file(value, new_name):
|
||||||
return file_object
|
return file_object
|
||||||
|
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def convert_image_format(value, new_format):
|
||||||
|
from wcs.fields import FileField
|
||||||
|
|
||||||
|
formats = {
|
||||||
|
'jpeg': 'image/jpeg',
|
||||||
|
'pdf': 'application/pdf',
|
||||||
|
'png': 'image/png',
|
||||||
|
}
|
||||||
|
if new_format not in formats:
|
||||||
|
get_publisher().record_error(
|
||||||
|
_('|convert_image_format: unknown format (must be one of %s)') % ', '.join(formats.keys())
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
file_object = FileField.convert_value_from_anything(value)
|
||||||
|
except ValueError:
|
||||||
|
file_object = None
|
||||||
|
if not file_object:
|
||||||
|
get_publisher().record_error(_('|convert_image_format: missing input'))
|
||||||
|
return None
|
||||||
|
|
||||||
|
if file_object.base_filename:
|
||||||
|
current_name, current_format = os.path.splitext(file_object.base_filename)
|
||||||
|
if current_format == f'.{new_format}':
|
||||||
|
return file_object
|
||||||
|
new_name = f'{current_name}.{new_format}'
|
||||||
|
else:
|
||||||
|
new_name = '%s.%s' % (_('file'), new_format)
|
||||||
|
|
||||||
|
try:
|
||||||
|
proc = subprocess.run(
|
||||||
|
['gm', 'convert', '-', f'{new_format}:-'],
|
||||||
|
input=file_object.get_content(),
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
get_publisher().record_error(_('|convert_image_format: not supported'))
|
||||||
|
return None
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
get_publisher().record_error(_('|convert_image_format: conversion error (%s)' % e.stderr.decode()))
|
||||||
|
return None
|
||||||
|
|
||||||
|
new_file_object = FileField.convert_value_from_anything(
|
||||||
|
{'content': proc.stdout, 'filename': new_name, 'content_type': formats[new_format]}
|
||||||
|
)
|
||||||
|
|
||||||
|
return new_file_object
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
def first(value):
|
def first(value):
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue