def crop(request): if request.method == "GET": return json_error(request, 'crop', action="cropping image", errors=["Form submission invalid"]) crop_form = CropForm(request.POST, request.FILES, prefix='crop') if not crop_form.is_valid(): return json_error(request, 'crop', action='submitting form', forms=[crop_form], log=True, exc_info=full_exc_info()) crop_data = copy.deepcopy(crop_form.cleaned_data) if crop_data.get('image_id'): db_image = Image.objects.get(pk=crop_data['image_id']) else: db_image = Image(image=crop_data['orig_image']) try: with db_image.image as f: f.open() pil_image = PIL.Image.open(BytesIO(f.read())) pil_image.filename = f.name except IOError: pil_image = None FormSet = modelformset_factory(Thumb, form=ThumbForm, formset=ThumbFormSet) thumb_formset = FormSet(request.POST, request.FILES, prefix='thumbs') if not thumb_formset.is_valid(): return json_error(request, 'crop', action='submitting form', formsets=[thumb_formset], log=True, exc_info=full_exc_info()) cropped_thumbs = thumb_formset.save(commit=False) non_model_fields = set(ThumbForm.declared_fields) - set( [f.name for f in Thumb._meta.fields]) # The fields we will pull from when populating the ThumbForm initial data json_thumb_fields = ['id', 'name', 'width', 'height'] thumbs_with_crops = [t for t in cropped_thumbs if t.crop_w and t.crop_h] thumbs_data = [f.cleaned_data for f in thumb_formset] standalone_mode = crop_data['standalone'] # Address a standalone mode issue where, because the thumbs don't have a pk value, # Django no longer returns them in Formset.save() if they are in initial_forms if standalone_mode and not cropped_thumbs and len( thumb_formset.initial_forms): thumb_form = thumb_formset.initial_forms[0] obj = thumb_form.instance cropped_thumbs = [ thumb_formset.save_existing(thumb_form, obj, commit=False) ] for i, (thumb, thumb_form) in enumerate(zip(cropped_thumbs, thumb_formset)): changed_fields = set(thumb_form.changed_data) - non_model_fields thumb_form._changed_data = list(changed_fields) thumb_data = thumbs_data[i] size = thumb_data['size'] if changed_fields & set(['crop_x', 'crop_y', 'crop_w', 'crop_h']): # Clear existing primary key to force new thumb creation thumb.pk = None thumb.width = min(filter(None, [thumb.width, thumb.crop_w])) thumb.height = min(filter(None, [thumb.height, thumb.crop_h])) try: new_thumbs = db_image.save_size(size, thumb, tmp=True, standalone=standalone_mode) except CropDusterResizeException as e: return json_error(request, 'crop', action="saving size", errors=[force_text(e)]) if not new_thumbs: continue if standalone_mode: thumb = new_thumbs new_thumbs = {thumb.name: thumb} cropped_thumbs[i] = thumb = new_thumbs.get(thumb.name, thumb) update_props = [ 'crop_x', 'crop_y', 'crop_w', 'crop_h', 'width', 'height', 'id', 'name' ] for prop in update_props: thumbs_data[i][prop] = getattr(thumb, prop) thumbs_data[i].update({ 'changed': True, 'url': db_image.get_image_url(thumb.name), }) for name, new_thumb in six.iteritems(new_thumbs): thumb_data = dict([(k, getattr(new_thumb, k)) for k in json_thumb_fields]) thumb_data['url'] = db_image.get_image_url( name, tmp=not (new_thumb.image_id)) crop_data['thumbs'].update({name: thumb_data}) if new_thumb.reference_thumb_id: continue thumbs_data[i]['thumbs'].update({name: thumb_data}) elif thumb.pk and thumb.name and thumb.crop_w and thumb.crop_h: thumb_path = db_image.get_image_path(thumb.name, tmp=False) tmp_thumb_path = db_image.get_image_path(thumb.name, tmp=True) if default_storage.exists(thumb_path): if not thumb_form.cleaned_data.get( 'changed') or not default_storage.exists( tmp_thumb_path): with default_storage.open(thumb_path) as f: with default_storage.open(tmp_thumb_path, 'wb') as tmp_file: tmp_file.write(f.read()) if not thumb.pk and not thumb.crop_w and not thumb.crop_h: if not len(thumbs_with_crops): continue best_fit = thumb_form.cleaned_data['size'].fit_to_crop( thumbs_with_crops[0], original_image=pil_image) if best_fit: thumbs_data[i].update({ 'crop_x': best_fit.box.x1, 'crop_y': best_fit.box.y1, 'crop_w': best_fit.box.w, 'crop_h': best_fit.box.h, 'changed': True, 'id': None, }) for thumb_data in thumbs_data: if isinstance(thumb_data['id'], Thumb): thumb_data['id'] = thumb_data['id'].pk preview_url = db_image.get_image_url('_preview') preview_w = PREVIEW_WIDTH preview_h = PREVIEW_HEIGHT orig_width, orig_height = crop_data['orig_w'], crop_data['orig_h'] if (orig_width and orig_height): resize_ratio = min(PREVIEW_WIDTH / float(orig_width), PREVIEW_HEIGHT / float(orig_height)) if resize_ratio < 1: preview_w = int(round(orig_width * resize_ratio)) preview_h = int(round(orig_height * resize_ratio)) return HttpResponse(json.dumps({ 'crop': crop_data, 'thumbs': thumbs_data, 'initial': True, 'preview_url': preview_url, 'preview_w': preview_w, 'preview_h': preview_h }), content_type='application/json')
def crop(request): if request.method == "GET": return json_error(request, 'crop', action="cropping image", errors=["Form submission invalid"]) crop_form = CropForm(request.POST, request.FILES, prefix='crop') if not crop_form.is_valid(): return json_error(request, 'crop', action='submitting form', forms=[crop_form], log=True, exc_info=full_exc_info()) crop_data = copy.deepcopy(crop_form.cleaned_data) db_image = Image(image=crop_data['orig_image']) try: pil_image = PIL.Image.open(db_image.image.path) except IOError: pil_image = None FormSet = modelformset_factory(Thumb, form=ThumbForm, formset=ThumbFormSet) thumb_formset = FormSet(request.POST, request.FILES, prefix='thumbs') if not thumb_formset.is_valid(): return json_error(request, 'crop', action='submitting form', formsets=[thumb_formset], log=True, exc_info=full_exc_info()) cropped_thumbs = thumb_formset.save(commit=False) non_model_fields = set(ThumbForm.declared_fields) - set([f.name for f in Thumb._meta.fields]) # The fields we will pull from when populating the ThumbForm initial data json_thumb_fields = ['id', 'name', 'width', 'height'] thumbs_with_crops = [t for t in cropped_thumbs if t.crop_w and t.crop_h] thumbs_data = [f.cleaned_data for f in thumb_formset] standalone_mode = crop_data['standalone'] # Address a standalone mode issue where, because the thumbs don't have a pk value, # Django no longer returns them in Formset.save() if they are in initial_forms if standalone_mode and not cropped_thumbs and len(thumb_formset.initial_forms): thumb_form = thumb_formset.initial_forms[0] obj = thumb_form.instance cropped_thumbs = [thumb_formset.save_existing(thumb_form, obj, commit=False)] for i, (thumb, thumb_form) in enumerate(zip(cropped_thumbs, thumb_formset)): changed_fields = set(thumb_form.changed_data) - non_model_fields thumb_form._changed_data = list(changed_fields) thumb_data = thumbs_data[i] size = thumb_data['size'] if changed_fields & set(['crop_x', 'crop_y', 'crop_w', 'crop_h']): # Clear existing primary key to force new thumb creation thumb.pk = None thumb.width = min(filter(None, [thumb.width, thumb.crop_w])) thumb.height = min(filter(None, [thumb.height, thumb.crop_h])) try: new_thumbs = db_image.save_size(size, thumb, tmp=True, standalone=standalone_mode) except CropDusterResizeException as e: return json_error(request, 'crop', action="saving size", errors=[force_text(e)]) if not new_thumbs: continue if standalone_mode: thumb = new_thumbs new_thumbs = {thumb.name: thumb} cropped_thumbs[i] = thumb = new_thumbs.get(thumb.name, thumb) update_props = ['crop_x', 'crop_y', 'crop_w', 'crop_h', 'width', 'height', 'id', 'name'] for prop in update_props: thumbs_data[i][prop] = getattr(thumb, prop) thumbs_data[i].update({ 'changed': True, 'url': db_image.get_image_url(thumb.name), }) for name, new_thumb in six.iteritems(new_thumbs): thumb_data = dict([(k, getattr(new_thumb, k)) for k in json_thumb_fields]) crop_data['thumbs'].update({name: thumb_data}) if new_thumb.reference_thumb_id: continue thumbs_data[i]['thumbs'].update({name: thumb_data}) elif thumb.pk and thumb.name and thumb.crop_w and thumb.crop_h: thumb_path = db_image.get_image_path(thumb.name, tmp=False) tmp_thumb_path = db_image.get_image_path(thumb.name, tmp=True) if os.path.exists(thumb_path): if not thumb_form.cleaned_data.get('changed') or not os.path.exists(tmp_thumb_path): shutil.copy(thumb_path, tmp_thumb_path) if not thumb.pk and not thumb.crop_w and not thumb.crop_h: if not len(thumbs_with_crops): continue best_fit = thumb_form.cleaned_data['size'].fit_to_crop( thumbs_with_crops[0], original_image=pil_image) if best_fit: thumbs_data[i].update({ 'crop_x': best_fit.box.x1, 'crop_y': best_fit.box.y1, 'crop_w': best_fit.box.w, 'crop_h': best_fit.box.h, 'changed': True, 'id': None, }) for thumb_data in thumbs_data: if isinstance(thumb_data['id'], Thumb): thumb_data['id'] = thumb_data['id'].pk return HttpResponse(json.dumps({ 'crop': crop_data, 'thumbs': thumbs_data, 'initial': True, }), content_type='application/json')
def upload(request): if request.method == 'GET': return index(request) # The data we'll be returning as JSON data = { 'warning': [], } form = UploadForm(request.POST, request.FILES) if not form.is_valid(): errors = form['image'].errors or form.errors return json_error(request, 'upload', action="uploading file", errors=[force_text(errors)]) form_data = form.cleaned_data is_standalone = bool(form_data.get('standalone')) orig_file_path = form_data['image'].name if six.PY2 and isinstance(orig_file_path, six.text_type): orig_file_path = orig_file_path.encode('utf-8') orig_image = get_relative_media_url(orig_file_path) with default_storage.open(orig_image, mode='rb') as f: img = PIL.Image.open(BytesIO(f.read())) img.filename = f.name (w, h) = (orig_w, orig_h) = img.size if is_animated_gif(img) and not has_animated_gif_support(): data['warning'].append( u"This server does not have animated gif support; your uploaded image " u"has been made static.") tmp_image = Image(image=orig_image) preview_w = form_data.get('preview_width') or PREVIEW_WIDTH preview_h = form_data.get('preview_height') or PREVIEW_HEIGHT # First pass resize if it's too large resize_ratio = min(preview_w / w, preview_h / h) def fit_preview(im): (w, h) = im.size if resize_ratio < 1: w = int(round(w * resize_ratio)) h = int(round(h * resize_ratio)) preview_img = im.resize((w, h), PIL.Image.ANTIALIAS) else: preview_img = im return preview_img if not is_standalone: preview_file_path = tmp_image.get_image_path('_preview') process_image(img, preview_file_path, fit_preview) data.update({ 'crop': { 'orig_image': orig_image, 'orig_w': orig_w, 'orig_h': orig_h, 'image_id': None, }, 'url': tmp_image.get_image_url('_preview'), 'orig_image': orig_image, 'orig_w': orig_w, 'orig_h': orig_h, 'width': w, 'height': h, }) if not is_standalone: return HttpResponse(json.dumps(data), content_type='application/json') size = Size('crop', w=img.size[0], h=img.size[1]) md5 = form_data.get('md5') try: standalone_image = StandaloneImage.objects.get(md5=md5) except StandaloneImage.DoesNotExist: standalone_image = StandaloneImage(md5=md5, image=orig_image) standalone_image.save() cropduster_image, created = Image.objects.get_or_create( content_type=ContentType.objects.get_for_model(StandaloneImage), object_id=standalone_image.pk) if not cropduster_image.image: cropduster_image.image = orig_image cropduster_image.save() elif cropduster_image.image.name != orig_image: data['crop']['orig_image'] = data[ 'orig_image'] = cropduster_image.image.name data['url'] = cropduster_image.get_image_url('_preview') with cropduster_image.image as f: f.open() img = PIL.Image.open(BytesIO(f.read())) img.filename = f.name preview_file_path = cropduster_image.get_image_path('_preview') if not default_storage.exists(preview_file_path): process_image(img, preview_file_path, fit_preview) thumb = cropduster_image.save_size(size, standalone=True, commit=False) sizes = form_data.get('sizes') or [] if len(sizes) == 1: size = sizes[0] else: size = Size('crop') data.update({ 'thumbs': [{ 'crop_x': thumb.crop_x, 'crop_y': thumb.crop_y, 'crop_w': thumb.crop_w, 'crop_h': thumb.crop_h, 'width': thumb.width, 'height': thumb.height, 'id': None, 'changed': True, 'size': json.dumps(size), 'name': thumb.name, }] }) data['crop'].update({ 'image_id': cropduster_image.pk, 'sizes': json.dumps([size]), }) return HttpResponse(json.dumps(data), content_type='application/json')
def upload(request): if request.method == 'GET': return index(request) # The data we'll be returning as JSON data = { 'warning': [], } form = UploadForm(request.POST, request.FILES) if not form.is_valid(): errors = form['image'].errors or form.errors return json_error(request, 'upload', action="uploading file", errors=[force_text(errors)]) form_data = form.cleaned_data is_standalone = bool(form_data.get('standalone')) orig_file_path = form_data['image'].name if six.PY2 and isinstance(orig_file_path, unicode): orig_file_path = orig_file_path.encode('utf-8') orig_image = get_relative_media_url(orig_file_path) img = PIL.Image.open(orig_file_path) (w, h) = (orig_w, orig_h) = img.size if is_animated_gif(img) and not has_animated_gif_support(): data['warning'].append( u"This server does not have animated gif support; your uploaded image " u"has been made static.") tmp_image = Image(image=orig_image) preview_w = form_data.get('preview_width') or PREVIEW_WIDTH preview_h = form_data.get('preview_height') or PREVIEW_HEIGHT # First pass resize if it's too large resize_ratio = min(preview_w / w, preview_h / h) def fit_preview(im): (w, h) = im.size if resize_ratio < 1: w = int(round(w * resize_ratio)) h = int(round(h * resize_ratio)) preview_img = im.resize((w, h), PIL.Image.ANTIALIAS) else: preview_img = im return preview_img if not is_standalone: preview_file_path = tmp_image.get_image_path('_preview') process_image(img, preview_file_path, fit_preview) data.update({ 'crop': { 'orig_image': orig_image, 'orig_w': orig_w, 'orig_h': orig_h, 'image_id': None, }, 'url': tmp_image.get_image_url('_preview'), 'orig_image': orig_image, 'orig_w': orig_w, 'orig_h': orig_h, 'width': w, 'height': h, }) if not is_standalone: return HttpResponse(json.dumps(data), content_type='application/json') size = Size('crop', w=img.size[0], h=img.size[1]) md5 = form_data.get('md5') try: standalone_image = StandaloneImage.objects.get(md5=md5) except StandaloneImage.DoesNotExist: standalone_image = StandaloneImage(md5=md5, image=orig_image) standalone_image.save() cropduster_image, created = Image.objects.get_or_create( content_type=ContentType.objects.get_for_model(StandaloneImage), object_id=standalone_image.pk) if not cropduster_image.image: cropduster_image.image = orig_image cropduster_image.save() elif cropduster_image.image.name != orig_image: data['crop']['orig_image'] = data['orig_image'] = cropduster_image.image.name data['url'] = cropduster_image.get_image_url('_preview') img = PIL.Image.open(cropduster_image.image.path) preview_file_path = cropduster_image.get_image_path('_preview') if not os.path.exists(preview_file_path): process_image(img, preview_file_path, fit_preview) thumb = cropduster_image.save_size(size, standalone=True) sizes = form_data.get('sizes') or [] if len(sizes) == 1: size = sizes[0] else: size = Size('crop') data.update({ 'thumbs': [{ 'crop_x': thumb.crop_x, 'crop_y': thumb.crop_y, 'crop_w': thumb.crop_w, 'crop_h': thumb.crop_h, 'width': thumb.width, 'height': thumb.height, 'id': None, 'changed': True, 'size': json.dumps(size), 'name': thumb.name, }] }) data['crop'].update({ 'image_id': cropduster_image.pk, 'sizes': json.dumps([size]), }) return HttpResponse(json.dumps(data), content_type='application/json')