root/CMFPlone_membership_hotfix/tags/1.0/utils.py

Revision 519 (checked in by chervol, 2 years ago)

initail import

  • Property svn:eol-style set to native
Line 
1 from cStringIO import StringIO
2
3 from PIL import Image
4
5
6
7 # Settings for member image resize quality
8 PIL_SCALING_ALGO = Image.ANTIALIAS
9 PIL_QUALITY = 88
10 MEMBER_IMAGE_SCALE = (75, 100)
11
12 def 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 browser.