--- a/src/pyams_file/image.py Sun Jul 19 02:01:21 2020 +0200
+++ b/src/pyams_file/image.py Mon Jul 20 14:53:39 2020 +0200
@@ -10,18 +10,27 @@
# FOR A PARTICULAR PURPOSE.
#
-__docformat__ = 'restructuredtext'
-
+import random
import re
+import sys
from io import BytesIO
from PIL import Image, ImageFilter
+from pyramid.renderers import render
+from zope.dublincore.interfaces import IZopeDublinCore
from zope.interface import implementer
from zope.schema.fieldproperty import FieldProperty
+from pyams_file.interfaces import IImage, IResponsiveImage, ISVGImage, IThumbnailGeometry, \
+ IThumbnailer, IThumbnails
+from pyams_utils.adapter import ContextAdapter, adapter_config
+from pyams_utils.request import check_request
+from pyams_utils.url import absolute_url
+
+
+__docformat__ = 'restructuredtext'
+
from pyams_file import _
-from pyams_file.interfaces import IImage, IResponsiveImage, IThumbnailGeometry, IThumbnailer, IThumbnails
-from pyams_utils.adapter import ContextAdapter, adapter_config
WEB_FORMATS = ('JPEG', 'PNG', 'GIF')
@@ -267,3 +276,62 @@
label = _("Large screen thumbnail")
weight = 13
+
+
+#
+# SVG utilities
+#
+
+def render_svg(image, width=None, height=None, request=None, css_class='', img_class=''):
+ """Render SVG file"""
+ options = {'svg': image}
+ if width or height:
+ options['style'] = 'width: {0}{1}; height: {2}{3};'.format(
+ width, 'px' if isinstance(width, int) else '',
+ height, 'px' if isinstance(height, int) else '')
+ else:
+ options['style'] = ''
+ options['css_class'] = css_class
+ result = render('templates/svg-render.pt', options, request)
+ if img_class:
+ result = result.replace('<svg ', '<svg class="{0}" '.format(img_class))
+ return result
+
+
+def render_img(image, width=None, height=None, request=None,
+ css_class='', img_class='', timestamp=False):
+ """Render image thumbnail"""
+ thumbnail = None
+ thumbnails = IThumbnails(image, None)
+ if thumbnails is not None:
+ if width and height:
+ thumbnail = thumbnails.get_thumbnail('{0}x{1}'.format(width, height))
+ elif width and (width != 'auto'):
+ thumbnail = thumbnails.get_thumbnail('w{0}'.format(width))
+ elif height and (height != 'auto'):
+ thumbnail = thumbnails.get_thumbnail('h{0}'.format(height))
+ if thumbnail is None:
+ thumbnail = image
+ if request is None:
+ request = check_request()
+ url = absolute_url(thumbnail, request)
+ if timestamp:
+ dc = IZopeDublinCore(thumbnail, None)
+ if dc is None:
+ timestamp = random.randint(0, sys.maxsize)
+ else:
+ timestamp = dc.modified.timestamp()
+ url += '?_={0}'.format(timestamp)
+ result = '<img src="{0}" class="{1}" />'.format(url, img_class)
+ if css_class:
+ result = '<div class="{0}">{1}</div>'.format(css_class, result)
+ return result
+
+
+def render_image(image, width=None, height=None, request=None,
+ css_class='', img_class='', timestamp=False):
+ """Render image"""
+ if ISVGImage.providedBy(image):
+ return render_svg(image, width, height, request, css_class, img_class)
+ else:
+ return render_img(image, width, height, request, css_class, img_class, timestamp)
--- a/src/pyams_file/thumbnail.py Sun Jul 19 02:01:21 2020 +0200
+++ b/src/pyams_file/thumbnail.py Mon Jul 20 14:53:39 2020 +0200
@@ -10,8 +10,6 @@
# FOR A PARTICULAR PURPOSE.
#
-__docformat__ = 'restructuredtext'
-
import logging
import re
@@ -21,20 +19,25 @@
from pyramid.events import subscriber
from pyramid.threadlocal import get_current_registry
from zope.interface import Interface, alsoProvides
-from zope.lifecycleevent import IObjectRemovedEvent, ObjectAddedEvent, ObjectCreatedEvent, ObjectRemovedEvent
+from zope.lifecycleevent import IObjectRemovedEvent, ObjectAddedEvent, ObjectCreatedEvent, \
+ ObjectRemovedEvent
from zope.location import locate
from zope.traversing.interfaces import ITraversable
from pyams_file.file import FileFactory
-from pyams_file.interfaces import IFileModifiedEvent, IImage, IMediaFile, IThumbnailFile, IThumbnailer, IThumbnails, \
- IWatermarker
-from pyams_file.zmi.image import render_image
-from pyams_utils.adapter import ContextAdapter, ContextRequestViewAdapter, adapter_config, get_annotation_adapter
+from pyams_file.image import render_image
+from pyams_file.interfaces import IFileModifiedEvent, IImage, IMediaFile, IThumbnailFile, \
+ IThumbnailer, IThumbnails, IWatermarker
+from pyams_utils.adapter import ContextAdapter, ContextRequestViewAdapter, adapter_config, \
+ get_annotation_adapter
from pyams_utils.interfaces.tales import ITALESExtension
from pyams_utils.registry import query_utility
from pyams_utils.request import check_request
+__docformat__ = 'restructuredtext'
+
+
logger = logging.getLogger('PyAMS (file)')
@@ -202,7 +205,8 @@
watermarker = query_utility(IWatermarker)
if watermarker is not None:
thumbnail_image.seek(0)
- thumbnail_image, format = watermarker.add_watermark(thumbnail_image, watermark)
+ thumbnail_image, format = watermarker.add_watermark(thumbnail_image,
+ watermark)
# create final image
thumbnail_image = FileFactory(thumbnail_image)
alsoProvides(thumbnail_image, IThumbnailFile)
@@ -263,7 +267,8 @@
return result
-@adapter_config(name='thumbnails', context=(Interface, Interface, Interface), provides=ITALESExtension)
+@adapter_config(name='thumbnails', context=(Interface, Interface, Interface),
+ provides=ITALESExtension)
class ThumbnailsExtension(ContextRequestViewAdapter):
"""extension:thumbnails(image) TALES extension
@@ -276,7 +281,8 @@
return IThumbnails(context, None)
-@adapter_config(name='thumbnail', context=(Interface, Interface, Interface), provides=ITALESExtension)
+@adapter_config(name='thumbnail', context=(Interface, Interface, Interface),
+ provides=ITALESExtension)
class ThumbnailExtension(ContextRequestViewAdapter):
"""extension:thumbnail(image, width, height, css_class, img_class) TALES extension
--- a/src/pyams_file/zmi/image.py Sun Jul 19 02:01:21 2020 +0200
+++ b/src/pyams_file/zmi/image.py Mon Jul 20 14:53:39 2020 +0200
@@ -10,14 +10,9 @@
# FOR A PARTICULAR PURPOSE.
#
-__docformat__ = 'restructuredtext'
-
-import random
-import sys
from collections import OrderedDict
import transaction
-from pyramid.renderers import render
from pyramid.view import view_config
from transaction.interfaces import ITransactionManager
from z3c.form import button, field
@@ -26,10 +21,9 @@
from zope.schema import Bool, Int
from zope.schema.fieldproperty import FieldProperty
-from pyams_file import _
from pyams_file.image import ThumbnailGeometry
-from pyams_file.interfaces import IFileInfo, IFileModifierForm, IImage, IMediaWidget, IResponsiveImage, ISVGImage, \
- IThumbnailForm, IThumbnailGeometry, IThumbnailer, IThumbnails
+from pyams_file.interfaces import IFileInfo, IFileModifierForm, IImage, IMediaWidget, \
+ IResponsiveImage, IThumbnailForm, IThumbnailGeometry, IThumbnailer, IThumbnails
from pyams_file.zmi import FileModifierAction
from pyams_form.form import DialogDisplayForm, ajax_config
from pyams_form.help import FormHelp
@@ -44,67 +38,15 @@
from pyams_template.template import template_config
from pyams_utils.adapter import adapter_config
from pyams_utils.interfaces import ICacheKeyValue, VIEW_PERMISSION
-from pyams_utils.request import check_request
from pyams_utils.url import absolute_url
from pyams_viewlet.viewlet import Viewlet, viewlet_config
from pyams_zmi.form import AdminDialogEditForm
from pyams_zmi.layer import IAdminLayer
-#
-# SVG utilities
-#
-
-def render_svg(image, width=None, height=None, request=None, css_class='', img_class=''):
- """Render SVG file"""
- options = {'svg': image}
- if width or height:
- options['style'] = 'width: {0}{1}; height: {2}{3};'.format(width, 'px' if isinstance(width, int) else '',
- height, 'px' if isinstance(height, int) else '')
- else:
- options['style'] = ''
- options['css_class'] = css_class
- result = render('templates/svg-render.pt', options, request)
- if img_class:
- result = result.replace('<svg ', '<svg class="{0}" '.format(img_class))
- return result
-
+__docformat__ = 'restructuredtext'
-def render_img(image, width=None, height=None, request=None, css_class='', img_class='', timestamp=False):
- """Render image thumbnail"""
- thumbnail = None
- thumbnails = IThumbnails(image, None)
- if thumbnails is not None:
- if width and height:
- thumbnail = thumbnails.get_thumbnail('{0}x{1}'.format(width, height))
- elif width and (width != 'auto'):
- thumbnail = thumbnails.get_thumbnail('w{0}'.format(width))
- elif height and (height != 'auto'):
- thumbnail = thumbnails.get_thumbnail('h{0}'.format(height))
- if thumbnail is None:
- thumbnail = image
- if request is None:
- request = check_request()
- url = absolute_url(thumbnail, request)
- if timestamp:
- dc = IZopeDublinCore(thumbnail, None)
- if dc is None:
- timestamp = random.randint(0, sys.maxsize)
- else:
- timestamp = dc.modified.timestamp()
- url += '?_={0}'.format(timestamp)
- result = '<img src="{0}" class="{1}" />'.format(url, img_class)
- if css_class:
- result = '<div class="{0}">{1}</div>'.format(css_class, result)
- return result
-
-
-def render_image(image, width=None, height=None, request=None, css_class='', img_class='', timestamp=False):
- """Render image"""
- if ISVGImage.providedBy(image):
- return render_svg(image, width, height, request, css_class, img_class)
- else:
- return render_img(image, width, height, request, css_class, img_class, timestamp)
+from pyams_file import _
#
@@ -121,7 +63,8 @@
@property
def url(self):
- return 'MyAMS.ajax.getJSON?url={0}'.format(absolute_url(self.context, self.request, 'rotate.json'))
+ return 'MyAMS.ajax.getJSON?url={0}'.format(
+ absolute_url(self.context, self.request, 'rotate.json'))
def get_url(self):
return self.url
@@ -145,9 +88,11 @@
'callback': 'MyAMS.skin.refreshContent',
'options': {
'object_id': 'thumbnail_{0}'.format(cache_key),
- 'content': '<img class="thumbnail" id="thumbnail_{key}" src="{src}" title="{title}" />'.format(
+ 'content': '<img class="thumbnail" id="thumbnail_{key}" '
+ 'src="{src}" title="{title}" />'.format(
key=cache_key,
- src='{0}?_={1}'.format(absolute_url(thumbnail, request), dc.modified.timestamp()),
+ src='{0}?_={1}'.format(absolute_url(thumbnail, request),
+ dc.modified.timestamp()),
title=II18n(image).query_attribute('title', request=request)
)
}