source: products/CMFPlone_membership_hotfix/tags/1.0/utils.py @ 1656

Last change on this file since 1656 was 1, checked in by myroslav, 18 years ago

Building directory structure

  • Property svn:eol-style set to native
File size: 4.2 KB
Line 
1from cStringIO import StringIO
2
3from PIL import Image
4
5
6
7# Settings for member image resize quality
8PIL_SCALING_ALGO = Image.ANTIALIAS
9PIL_QUALITY = 88
10MEMBER_IMAGE_SCALE = (75, 100)
11
12def scale_image(image_file, max_size=MEMBER_IMAGE_SCALE,
13                default_format = 'PNG'):
14    """Scales an image down to at most max_size preserving aspect ratio
15    from an input file
16
17        >>> import Products.CMFPlone
18        >>> import os
19        >>> from StringIO import StringIO
20        >>> from Products.CMFPlone.utils import scale_image
21        >>> from PIL import Image
22
23    Let's make a couple test images and see how it works (all are
24    100x100), the gif is palletted mode::
25
26        >>> plone_path = os.path.dirname(Products.CMFPlone.__file__)
27        >>> pjoin = os.path.join
28        >>> path = pjoin(plone_path, 'tests', 'images')
29        >>> orig_jpg = open(pjoin(path, 'test.jpg'))
30        >>> orig_png = open(pjoin(path, 'test.png'))
31        >>> orig_gif = open(pjoin(path, 'test.gif'))
32
33    We'll also make some evil non-images, including one which
34    masquerades as a jpeg (which would trick OFS.Image)::
35
36        >>> invalid = StringIO('<div>Evil!!!</div>')
37        >>> sneaky = StringIO('\377\330<div>Evil!!!</div>')
38
39    OK, let's get to it, first check that our bad images fail:
40
41        >>> scale_image(invalid, (50, 50))
42        Traceback (most recent call last):
43        ...
44        IOError: cannot identify image file
45        >>> scale_image(sneaky, (50, 50))
46        Traceback (most recent call last):
47        ...
48        IOError: cannot identify image file
49
50    Now that that's out of the way we check on our real images to make
51    sure the format and mode are preserved, that they are scaled, and that they
52    return the correct mimetype::
53
54        >>> new_jpg, mimetype = scale_image(orig_jpg, (50, 50))
55        >>> img = Image.open(new_jpg)
56        >>> img.size
57        (50, 50)
58        >>> img.format
59        'JPEG'
60        >>> mimetype
61        'image/jpeg'
62
63        >>> new_png, mimetype = scale_image(orig_png, (50, 50))
64        >>> img = Image.open(new_png)
65        >>> img.size
66        (50, 50)
67        >>> img.format
68        'PNG'
69        >>> mimetype
70        'image/png'
71
72        >>> new_gif, mimetype = scale_image(orig_gif, (50, 50))
73        >>> img = Image.open(new_gif)
74        >>> img.size
75        (50, 50)
76        >>> img.format
77        'GIF'
78        >>> img.mode
79        'P'
80        >>> mimetype
81        'image/gif'
82
83    We should also preserve the aspect ratio by scaling to the given
84    width only unless told not to (we need to reset out files before
85    trying again though::
86
87        >>> orig_jpg.seek(0)
88        >>> new_jpg, mimetype = scale_image(orig_jpg, (70, 100))
89        >>> img = Image.open(new_jpg)
90        >>> img.size
91        (70, 70)
92
93        >>> orig_jpg.seek(0)
94        >>> new_jpg, mimetype = scale_image(orig_jpg, (70, 50))
95        >>> img = Image.open(new_jpg)
96        >>> img.size
97        (50, 50)
98
99    """
100    # Make sure we have ints
101    size = (int(max_size[0]), int(max_size[1]))
102    # Load up the image, don't try to catch errors, we want to fail miserably
103    # on invalid images
104    image = Image.open(image_file)
105    # When might image.format not be true?
106    format = image.format
107    mimetype = 'image/%s'%format.lower()
108    cur_size = image.size
109    # from Archetypes ImageField
110    # consider image mode when scaling
111    # source images can be mode '1','L,','P','RGB(A)'
112    # convert to greyscale or RGBA before scaling
113    # preserve palletted mode (but not pallette)
114    # for palletted-only image formats, e.g. GIF
115    # PNG compression is OK for RGBA thumbnails
116    original_mode = image.mode
117    if original_mode == '1':
118        image = image.convert('L')
119    elif original_mode == 'P':
120        image = image.convert('RGBA')
121    # Rescale in place with an method that will not alter the aspect ratio
122    # and will only shrink the image not enlarge it.
123    image.thumbnail(size, resample=PIL_SCALING_ALGO)
124    # preserve palletted mode for GIF and PNG
125    if original_mode == 'P' and format in ('GIF', 'PNG'):
126        image = image.convert('P')
127    # Save
128    new_file = StringIO()
129    image.save(new_file, format, quality=PIL_QUALITY)
130    new_file.seek(0)
131    # Return the file data and the new mimetype
132    return new_file, mimetype
133
Note: See TracBrowser for help on using the repository browser.