source: products/CMFPlone_membership_hotfix/trunk/utils.py @ 1656

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

Building directory structure

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