From 5b4b28179163a31ba50acc5cd82a9e730e7b39b9 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Thu, 21 Apr 2011 23:58:41 -0400 Subject: [PATCH] Added interactive document page view rotation support --- apps/converter/api.py | 14 ++++-- apps/documents/__init__.py | 5 +- apps/documents/conf/settings.py | 1 + apps/documents/forms.py | 10 +++- apps/documents/urls.py | 2 + apps/documents/views.py | 83 +++++++++++++++++++++++++++------ apps/main/templates/base.html | 2 +- apps/main/views.py | 1 + settings.py | 1 + 9 files changed, 96 insertions(+), 23 deletions(-) diff --git a/apps/converter/api.py b/apps/converter/api.py index b475cb848e..7d93c51bfb 100644 --- a/apps/converter/api.py +++ b/apps/converter/api.py @@ -102,17 +102,17 @@ def create_image_cache_filename(input_filepath, *args, **kwargs): return None -def in_image_cache(input_filepath, size, page=0, format=u'jpg', quality=QUALITY_DEFAULT, extra_options=u'', zoom=100): - output_filepath = create_image_cache_filename(input_filepath, size=size, page=page, format=format, quality=quality, extra_options=extra_options, zoom=zoom) +def in_image_cache(input_filepath, size, page=0, format=u'jpg', quality=QUALITY_DEFAULT, extra_options=u'', zoom=100, rotation=0): + output_filepath = create_image_cache_filename(input_filepath, size=size, page=page, format=format, quality=quality, extra_options=extra_options, zoom=zoom, rotation=rotation) if os.path.exists(output_filepath): return output_filepath else: return None -def convert(input_filepath, size, quality=QUALITY_DEFAULT, cache=True, page=0, format=u'jpg', extra_options=u'', mimetype=None, extension=None, cleanup_files=True, zoom=100): +def convert(input_filepath, size, quality=QUALITY_DEFAULT, cache=True, page=0, format=u'jpg', extra_options=u'', mimetype=None, extension=None, cleanup_files=True, zoom=100, rotation=0): unoconv_output = None - output_filepath = create_image_cache_filename(input_filepath, size=size, page=page, format=format, quality=quality, extra_options=extra_options, zoom=zoom) + output_filepath = create_image_cache_filename(input_filepath, size=size, page=page, format=format, quality=quality, extra_options=extra_options, zoom=zoom, rotation=rotation) if os.path.exists(output_filepath): return output_filepath ''' @@ -130,7 +130,11 @@ def convert(input_filepath, size, quality=QUALITY_DEFAULT, cache=True, page=0, f input_arg = u'%s[%s]' % (input_filepath, page) extra_options += u' -resize %s' % size if zoom != 100: - extra_options += ' -resize %d%% ' % zoom + extra_options += u' -resize %d%% ' % zoom + + if rotation != 0 and rotation != 360: + extra_options += u' -rotate %d ' % rotation + backend.execute_convert(input_filepath=input_arg, arguments=extra_options, output_filepath=u'%s:%s' % (format, output_filepath), quality=quality) finally: if cleanup_files: diff --git a/apps/documents/__init__.py b/apps/documents/__init__.py index db7a9d9574..8debf1f8f1 100644 --- a/apps/documents/__init__.py +++ b/apps/documents/__init__.py @@ -72,6 +72,8 @@ document_page_navigation_first = {'text': _('first page'), 'view': 'document_pag document_page_navigation_last = {'text': _('last page'), 'view': 'document_page_navigation_last', 'args': 'object.id', 'famfam': 'resultset_last', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} document_page_zoom_in = {'text': _('zoom in'), 'view': 'document_page_zoom_in', 'args': 'object.id', 'famfam': 'zoom_in', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} document_page_zoom_out = {'text': _('zoom out'), 'view': 'document_page_zoom_out', 'args': 'object.id', 'famfam': 'zoom_out', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} +document_page_rotate_right = {'text': _('rotate right'), 'view': 'document_page_rotate_right', 'args': 'object.id', 'famfam': 'arrow_turn_right', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} +document_page_rotate_left = {'text': _('rotate left'), 'view': 'document_page_rotate_left', 'args': 'object.id', 'famfam': 'arrow_turn_left', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} document_missing_list = {'text': _('Find missing document files'), 'view': 'document_missing_list', 'famfam': 'folder_page', 'permissions': {'namespace': 'documents', 'permissions': [PERMISSION_DOCUMENT_VIEW]}} @@ -95,8 +97,7 @@ register_links(DocumentPage, [ document_page_navigation_next, document_page_navigation_last ]) -register_links(['document_page_view'], [document_page_zoom_in, document_page_zoom_out], menu_name='sidebar') - +register_links(['document_page_view'], [document_page_rotate_right, document_page_rotate_left, document_page_zoom_in, document_page_zoom_out], menu_name='form_header') register_links(DocumentPageTransformation, [document_page_transformation_edit, document_page_transformation_delete]) register_links(DocumentPageTransformation, [document_page_transformation_page_edit, document_page_transformation_page_view], menu_name='sidebar') diff --git a/apps/documents/conf/settings.py b/apps/documents/conf/settings.py index 973fef1c56..6dcc4f8b08 100644 --- a/apps/documents/conf/settings.py +++ b/apps/documents/conf/settings.py @@ -66,6 +66,7 @@ RECENT_COUNT = getattr(settings, 'DOCUMENTS_RECENT_COUNT', 20) ZOOM_PERCENT_STEP = getattr(settings, 'DOCUMENTS_ZOOM_PERCENT_STEP', 50) ZOOM_MAX_LEVEL = getattr(settings, 'DOCUMENTS_ZOOM_MAX_LEVEL', 200) ZOOM_MIN_LEVEL = getattr(settings, 'DOCUMENTS_ZOOM_MIN_LEVEL', 50) +ROTATION_STEP = getattr(settings, 'DOCUMENTS_ROTATION_STEP', 90) # Transformations AVAILABLE_TRANSFORMATIONS = getattr(settings, 'DOCUMENTS_AVAILABLE_TRANSFORMATIONS', available_transformations) diff --git a/apps/documents/forms.py b/apps/documents/forms.py index f301e2d19c..82df934e49 100644 --- a/apps/documents/forms.py +++ b/apps/documents/forms.py @@ -32,12 +32,14 @@ class DocumentPageImageWidget(forms.widgets.Widget): def render(self, name, value, attrs=None): final_attrs = self.build_attrs(attrs) zoom = final_attrs.get('zoom', 100) + rotation = final_attrs.get('rotation', 0) if value: output = [] - output.append('
' % { + output.append('
' % { 'img': reverse('document_display', args=[value.document.id]), 'page': value.page_number, 'zoom': zoom, + 'rotation': rotation, }) return mark_safe(u''.join(output)) else: @@ -51,9 +53,13 @@ class DocumentPageForm(DetailForm): def __init__(self, *args, **kwargs): zoom = kwargs.pop('zoom', 100) + rotation = kwargs.pop('rotation', 0) super(DocumentPageForm, self).__init__(*args, **kwargs) self.fields['page_image'].initial = self.instance - self.fields['page_image'].widget.attrs.update({'zoom': zoom}) + self.fields['page_image'].widget.attrs.update({ + 'zoom': zoom, + 'rotation': rotation + }) page_image = forms.CharField(widget=DocumentPageImageWidget()) diff --git a/apps/documents/urls.py b/apps/documents/urls.py index 8dace3bb01..7ab697452f 100644 --- a/apps/documents/urls.py +++ b/apps/documents/urls.py @@ -50,6 +50,8 @@ urlpatterns = patterns('documents.views', url(r'^document/page/(?P\d+)/navigation/last/$', 'document_page_navigation_last', (), 'document_page_navigation_last'), url(r'^document/page/(?P\d+)/zoom/in/$', 'document_page_zoom_in', (), 'document_page_zoom_in'), url(r'^document/page/(?P\d+)/zoom/out/$', 'document_page_zoom_out', (), 'document_page_zoom_out'), + url(r'^document/page/(?P\d+)/rotate/right/$', 'document_page_rotate_right', (), 'document_page_rotate_right'), + url(r'^document/page/(?P\d+)/rotate/left/$', 'document_page_rotate_left', (), 'document_page_rotate_left'), url(r'^document/page/(?P\d+)/transformation/list/$', 'document_page_transformation_list', (), 'document_page_transformation_list'), url(r'^document/page/(?P\d+)/transformation/create/$', 'document_page_transformation_create', (), 'document_page_transformation_create'), diff --git a/apps/documents/views.py b/apps/documents/views.py index 91fcae87bd..8e4b6a2827 100644 --- a/apps/documents/views.py +++ b/apps/documents/views.py @@ -1,5 +1,6 @@ import zipfile import urlparse +import urllib from django.utils.translation import ugettext as _ from django.http import HttpResponse, HttpResponseRedirect @@ -39,6 +40,7 @@ from documents.conf.settings import STORAGE_BACKEND from documents.conf.settings import ZOOM_PERCENT_STEP from documents.conf.settings import ZOOM_MAX_LEVEL from documents.conf.settings import ZOOM_MIN_LEVEL +from documents.conf.settings import ROTATION_STEP from documents import PERMISSION_DOCUMENT_CREATE, \ PERMISSION_DOCUMENT_CREATE, PERMISSION_DOCUMENT_PROPERTIES_EDIT, \ @@ -550,16 +552,15 @@ def get_document_image(request, document_id, size=PREVIEW_SIZE, quality=QUALITY_ tranformation_string = ' '.join(transformation_list) zoom = int(request.GET.get('zoom', 100)) - if zoom > 200: - zoom = 200 - if zoom < 1: - zoom = 1 - - rotation = int(request.GET.get('rotation', 0)) - if rotation > 360 or rotation < 0: - rotation %= 360 - + if zoom < ZOOM_MIN_LEVEL: + zoom = ZOOM_MIN_LEVEL + + if zoom > ZOOM_MAX_LEVEL: + zoom = ZOOM_MAX_LEVEL + + rotation = int(request.GET.get('rotation', 0)) % 360 + try: filepath = in_image_cache(document.checksum, size=size, quality=quality, extra_options=tranformation_string, page=page - 1, zoom=zoom, rotation=rotation) if filepath: @@ -1034,12 +1035,18 @@ def document_page_zoom_in(request, document_page_id): # Parse the query string and get the zoom value # parse_qs return a dictionary whose values are lists zoom = int(urlparse.parse_qs(query).get('zoom', ['100'])[0]) + rotation = int(urlparse.parse_qs(query).get('rotation', ['0'])[0]) zoom += ZOOM_PERCENT_STEP if zoom > ZOOM_MAX_LEVEL: zoom = ZOOM_MAX_LEVEL - - return HttpResponseRedirect(reverse(view, args=[document_page.pk]) + u'?zoom=%s' % zoom) + + return HttpResponseRedirect( + u'?'.join([ + reverse(view, args=[document_page.pk]), + urllib.urlencode({'zoom': zoom, 'rotation': rotation}) + ]) + ) def document_page_zoom_out(request, document_page_id): @@ -1052,9 +1059,59 @@ def document_page_zoom_out(request, document_page_id): # Parse the query string and get the zoom value # parse_qs return a dictionary whose values are lists zoom = int(urlparse.parse_qs(query).get('zoom', ['100'])[0]) - print 'zoom', zoom + rotation = int(urlparse.parse_qs(query).get('rotation', ['0'])[0]) + zoom -= ZOOM_PERCENT_STEP if zoom < ZOOM_MIN_LEVEL: zoom = ZOOM_MIN_LEVEL - return HttpResponseRedirect(reverse(view, args=[document_page.pk]) + u'?zoom=%s' % zoom) + return HttpResponseRedirect( + u'?'.join([ + reverse(view, args=[document_page.pk]), + urllib.urlencode({'zoom': zoom, 'rotation': rotation}) + ]) + ) + + +def document_page_rotate_right(request, document_page_id): + check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) + view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).path) + + document_page = get_object_or_404(DocumentPage, pk=document_page_id) + # Get the query string from the referer url + query = urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).query + # Parse the query string and get the zoom value + # parse_qs return a dictionary whose values are lists + zoom = int(urlparse.parse_qs(query).get('zoom', ['100'])[0]) + rotation = int(urlparse.parse_qs(query).get('rotation', ['0'])[0]) + + rotation = (rotation + ROTATION_STEP) % 360 + + return HttpResponseRedirect( + u'?'.join([ + reverse(view, args=[document_page.pk]), + urllib.urlencode({'zoom': zoom, 'rotation': rotation}) + ]) + ) + + +def document_page_rotate_left(request, document_page_id): + check_permissions(request.user, 'documents', [PERMISSION_DOCUMENT_VIEW]) + view = resolve_to_name(urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).path) + + document_page = get_object_or_404(DocumentPage, pk=document_page_id) + # Get the query string from the referer url + query = urlparse.urlparse(request.META.get('HTTP_REFERER', '/')).query + # Parse the query string and get the zoom value + # parse_qs return a dictionary whose values are lists + zoom = int(urlparse.parse_qs(query).get('zoom', ['100'])[0]) + rotation = int(urlparse.parse_qs(query).get('rotation', ['0'])[0]) + + rotation = (rotation - ROTATION_STEP) % 360 + + return HttpResponseRedirect( + u'?'.join([ + reverse(view, args=[document_page.pk]), + urllib.urlencode({'zoom': zoom, 'rotation': rotation}) + ]) + ) diff --git a/apps/main/templates/base.html b/apps/main/templates/base.html index 0fe0ab1aa5..8cb3ab49c8 100644 --- a/apps/main/templates/base.html +++ b/apps/main/templates/base.html @@ -80,7 +80,7 @@ 'margin' : 20 }); $('.scrollable').scrollview(); - $('.full-height').height($(window).height() - 175); + $('.full-height').height($(window).height() - 210); }); diff --git a/apps/main/views.py b/apps/main/views.py index b9c7b399bf..d22f461bab 100644 --- a/apps/main/views.py +++ b/apps/main/views.py @@ -49,6 +49,7 @@ def check_settings(request): {'name': 'DOCUMENTS_ZOOM_PERCENT_STEP', 'value': documents_settings.ZOOM_PERCENT_STEP}, {'name': 'DOCUMENTS_ZOOM_MAX_LEVEL', 'value': documents_settings.ZOOM_MAX_LEVEL}, {'name': 'DOCUMENTS_ZOOM_MIN_LEVEL', 'value': documents_settings.ZOOM_MIN_LEVEL}, + {'name': 'DOCUMENTS_ROTATION_STEP', 'value': documents_settings.ROTATION_STEP}, #Groups {'name': 'DOCUMENTS_GROUP_MAX_RESULTS', 'value': documents_settings.GROUP_MAX_RESULTS}, diff --git a/settings.py b/settings.py index dd64379da8..e9a03ff581 100644 --- a/settings.py +++ b/settings.py @@ -197,6 +197,7 @@ TEMPLATE_CONTEXT_PROCESSORS = ( #DOCUMENTS_ZOOM_PERCENT_STEP = 50 #DOCUMENTS_ZOOM_MAX_LEVEL = 200 #DOCUMENTS_ZOOM_MIN_LEVEL = 50 +#DOCUMENTS_ROTATION_STEP = 90 # Groups #DOCUMENTS_GROUP_MAX_RESULTS = 20