misc: add support for thumbnailing PDF files (#26632)
This commit is contained in:
parent
13383f60c1
commit
45fa82bc3f
|
@ -25,7 +25,8 @@ Recommends: python-dns,
|
|||
python-qrcode,
|
||||
libjs-leaflet,
|
||||
python-magic,
|
||||
python-docutils
|
||||
python-docutils,
|
||||
graphicsmagick
|
||||
Suggests: python-libxml2,
|
||||
python-lasso,
|
||||
python-psycopg2
|
||||
|
|
|
@ -33,7 +33,7 @@ from django.utils.formats import date_format as django_date_format
|
|||
from qommon import _
|
||||
from qommon import evalutils
|
||||
from qommon.form import *
|
||||
from qommon.misc import localstrftime, strftime, date_format, ellipsize
|
||||
from qommon.misc import localstrftime, strftime, date_format, ellipsize, can_thumbnail
|
||||
from qommon.template import Template, TemplateError
|
||||
from qommon import get_cfg, get_logger
|
||||
|
||||
|
@ -907,7 +907,7 @@ class FileField(WidgetField):
|
|||
t = TemplateIO(html=True)
|
||||
t += htmltext('<div class="file-field">')
|
||||
t += htmltext('<a download="%s" href="[download]?f=%s">') % (value.base_filename, self.id)
|
||||
if value.content_type and value.content_type.startswith('image/'):
|
||||
if can_thumbnail(value.content_type):
|
||||
t += htmltext('<img alt="" src="[download]?f=%s&thumbnail=1"/>') % self.id
|
||||
t += htmltext('<span>%s</span>') % value
|
||||
t += htmltext('</a></div>')
|
||||
|
|
|
@ -81,17 +81,16 @@ class FileDirectory(Directory):
|
|||
response.set_header(
|
||||
'content-disposition', 'attachment; filename="%s"' % file.base_filename)
|
||||
|
||||
fp = file.get_file_pointer()
|
||||
if self.thumbnails and file.content_type.startswith('image/'):
|
||||
if self.thumbnails and misc.can_thumbnail(file.content_type):
|
||||
try:
|
||||
thumbnail = misc.get_thumbnail(fp)
|
||||
except misc.ThumbnailError:
|
||||
pass
|
||||
else:
|
||||
thumbnail = misc.get_thumbnail(file.get_filename(),
|
||||
content_type=file.content_type)
|
||||
response.set_content_type('image/png')
|
||||
return thumbnail
|
||||
except misc.ThumbnailError:
|
||||
pass
|
||||
|
||||
return fp.read()
|
||||
return file.get_file_pointer().read()
|
||||
|
||||
|
||||
class FilesDirectory(Directory):
|
||||
|
|
|
@ -1123,16 +1123,16 @@ class FormPage(Directory, FormTemplateMixin):
|
|||
if tempfile['charset']:
|
||||
response.set_charset(tempfile['charset'])
|
||||
|
||||
file_pointer = get_session().get_tempfile_content(t).get_file_pointer()
|
||||
if get_request().form.get('thumbnail') == '1':
|
||||
try:
|
||||
thumbnail = misc.get_thumbnail(file_pointer)
|
||||
thumbnail = misc.get_thumbnail(get_session().get_tempfile_path(t),
|
||||
content_type=tempfile['content_type'])
|
||||
except misc.ThumbnailError:
|
||||
pass
|
||||
else:
|
||||
response.set_content_type('image/png')
|
||||
return thumbnail
|
||||
return file_pointer.read()
|
||||
return get_session().get_tempfile_content(t).get_file_pointer().read()
|
||||
|
||||
def validating(self, data):
|
||||
self.html_top(self.formdef.name)
|
||||
|
|
|
@ -71,7 +71,7 @@ from wcs.conditions import Condition, ValidationError
|
|||
from qommon import _, ngettext
|
||||
import misc
|
||||
from .humantime import humanduration2seconds, seconds2humanduration, timewords
|
||||
from .misc import strftime, C_
|
||||
from .misc import strftime, C_, HAS_GM
|
||||
from publisher import get_cfg
|
||||
from .template_utils import render_block_to_string
|
||||
|
||||
|
@ -698,8 +698,11 @@ class FileWithPreviewWidget(CompositeWidget):
|
|||
if not temp:
|
||||
return False
|
||||
|
||||
filetype = mimetypes.guess_type(temp.get('orig_filename', ''))
|
||||
if not (filetype and filetype[0] and filetype[0].startswith('image')):
|
||||
filetype = (mimetypes.guess_type(temp.get('orig_filename', '')) or [''])[0]
|
||||
if filetype == 'application/pdf':
|
||||
return HAS_GM
|
||||
|
||||
if not filetype.startswith('image/'):
|
||||
return False
|
||||
|
||||
if Image:
|
||||
|
@ -821,6 +824,12 @@ class PicklableUpload(Upload):
|
|||
# quack like UploadedFile
|
||||
return self.get_file_pointer()
|
||||
|
||||
def get_filename(self):
|
||||
if not hasattr(self, 'qfilename'):
|
||||
raise AttributeError('filename')
|
||||
basedir = os.path.join(get_publisher().app_dir, 'uploads')
|
||||
return os.path.join(basedir, self.qfilename)
|
||||
|
||||
def get_content(self):
|
||||
if hasattr(self, 'qfilename'):
|
||||
filename = os.path.join(get_publisher().app_dir, 'uploads', self.qfilename)
|
||||
|
|
|
@ -51,6 +51,12 @@ from urllib import urlencode, quote
|
|||
from urllib2 import urlparse
|
||||
from cStringIO import StringIO
|
||||
|
||||
try:
|
||||
subprocess.check_call(['which', 'gm'], stdout=open('/dev/null', 'w'))
|
||||
HAS_GM = True
|
||||
except subprocess.CalledProcessError:
|
||||
HAS_GM = False
|
||||
|
||||
|
||||
class ThumbnailError(Exception):
|
||||
pass
|
||||
|
@ -553,19 +559,32 @@ def file_digest(content, chunk_size=100000):
|
|||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
def get_thumbnail(fp):
|
||||
if Image is None:
|
||||
|
||||
def can_thumbnail(content_type):
|
||||
if content_type == 'application/pdf' and not (HAS_GM and Image):
|
||||
return False
|
||||
if content_type and content_type.startswith('image/'):
|
||||
return bool(Image is not None)
|
||||
return False
|
||||
|
||||
|
||||
def get_thumbnail(filepath, content_type=None):
|
||||
if not can_thumbnail(content_type or ''):
|
||||
raise ThumbnailError()
|
||||
|
||||
if content_type == 'application/pdf':
|
||||
fp = StringIO(subprocess.check_output(
|
||||
['gm', 'convert', '-geometry', '500x', filepath, 'png:-']))
|
||||
else:
|
||||
fp = open(filepath)
|
||||
|
||||
try:
|
||||
image = Image.open(fp)
|
||||
image.thumbnail((500, 300))
|
||||
image_thumb_fp = StringIO()
|
||||
image.save(image_thumb_fp, "PNG")
|
||||
except IOError:
|
||||
# failed to create thumbnail; restore file pointer state and raise
|
||||
# exception.
|
||||
fp.seek(0)
|
||||
# failed to create thumbnail.
|
||||
raise ThumbnailError()
|
||||
return image_thumb_fp.getvalue()
|
||||
|
||||
|
|
|
@ -303,6 +303,14 @@ class Session(QommonSession, CaptchaSession, StorableObject):
|
|||
return None
|
||||
return misc.json_loads(open(filename).read())
|
||||
|
||||
def get_tempfile_path(self, token):
|
||||
temp = self.get_tempfile(token)
|
||||
if not temp:
|
||||
return None
|
||||
dirname = os.path.join(get_publisher().app_dir, 'tempfiles')
|
||||
filename = os.path.join(dirname, temp['unsigned_token'])
|
||||
return filename
|
||||
|
||||
def get_tempfile_content(self, token):
|
||||
temp = self.get_tempfile(token)
|
||||
if not temp:
|
||||
|
|
Loading…
Reference in New Issue