Example #1
0
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')
Example #3
0
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')