1 # -*- coding: utf-8 -*-
2 #######################################################################################
3 # Photo is a part of Plinn - http://plinn.org #
4 # Copyright (C) 2004-2007 Benoît PIN <benoit.pin@ensmp.fr> #
6 # This program is free software; you can redistribute it and/or #
7 # modify it under the terms of the GNU General Public License #
8 # as published by the Free Software Foundation; either version 2 #
9 # of the License, or (at your option) any later version. #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program; if not, write to the Free Software #
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #
19 #######################################################################################
20 """ Tile support module
26 from AccessControl
import ClassSecurityInfo
27 from AccessControl
import Unauthorized
28 from AccessControl
import getSecurityManager
29 from AccessControl
.Permissions
import view
, change_images_and_files
30 from PIL
import Image
as PILImage
32 from blobbases
import Image
33 from xmputils
import TIFF_ORIENTATIONS
34 from cache
import memoizedmethod
35 from BTrees
.OOBTree
import OOBTree
36 from BTrees
.IOBTree
import IOBTree
37 from ppm
import PPMFile
38 from threading
import Lock
39 from subprocess
import Popen
, PIPE
40 from tempfile
import TemporaryFile
42 JPEG_ROTATE
= 'jpegtran -rotate %d'
43 JPEG_FLIP
= 'jpegtran -flip horizontal'
46 """ Decorator. exit if already running """
49 def method(*args
, **kw
):
50 if not lock
.locked() :
64 """ Mixin class to generate tiles from image """
66 security
= ClassSecurityInfo()
68 tileGenerationLock
= Lock()
71 self
._tiles
= OOBTree()
73 security
.declarePrivate('makeTilesAt')
74 @runOnce(tileGenerationLock
)
75 def makeTilesAt(self
, zoom
):
76 """generates tiles at zoom level"""
78 if self
._tiles
.has_key(zoom
) :
81 assert zoom
<= 1, "zoom arg must be <= 1 found: %s" % zoom
85 ppm
= ppm
.resize(ratio
=zoom
)
87 self
._makeTilesAt
(zoom
, ppm
)
91 bf
= self
._getJpegBlob
()
94 orientation
= self
.tiffOrientation()
95 rotation
, flip
= TIFF_ORIENTATIONS
.get(orientation
, (0, False))
97 if rotation
and flip
:
98 tf
= TemporaryFile(mode
='w+')
99 pRot
= Popen(JPEG_ROTATE
% rotation
103 pFlip
= Popen(JPEG_FLIP
113 tf
= TemporaryFile(mode
='w+')
114 pRot
= Popen(JPEG_ROTATE
% rotation
124 tf
= TemporaryFile(mode
='w+')
125 pFlip
= Popen(JPEG_FLIP
134 ppm
= PPMFile(f
, tileSize
=self
.tileSize
)
138 def _makeTilesAt(self
, zoom
, ppm
):
139 hooks
= self
._getAfterTilingHooks
()
140 self
._tiles
[zoom
] = IOBTree()
141 bgColor
= getattr(self
, 'tiles_background_color', '#fff')
143 for x
in xrange(ppm
.tilesX
) :
144 self
._tiles
[zoom
][x
] = IOBTree()
145 for y
in xrange(ppm
.tilesY
) :
146 tile
= ppm
.getTile(x
, y
)
150 # fill with solid color
151 if min(tile
.size
) < self
.tileSize
:
152 blankTile
= PILImage
.new('RGB', (self
.tileSize
, self
.tileSize
), bgColor
)
153 box
= (0,0) + tile
.size
154 blankTile
.paste(tile
, box
)
157 zImg
= Image('tile', 'tile', '', content_type
='image/jpeg')
159 tile
.save(out
, 'JPEG', quality
=90)
160 zImg
.updateFormat(out
.tell(), tile
.size
, 'image/jpeg')
163 self
._tiles
[zoom
][x
][y
] = zImg
165 def _getAfterTilingHooks(self
) :
169 security
.declareProtected(view
, 'getAvailableZooms')
170 def getAvailableZooms(self
):
171 zooms
= list(self
._tiles
.keys())
175 security
.declareProtected(view
, 'getTile')
176 def getTile(self
, REQUEST
, RESPONSE
, zoom
=1, x
=0, y
=0):
179 zoom
, x
, y
= float(zoom
), int(x
), int(y
)
180 if not self
._tiles
.has_key(zoom
) :
181 sm
= getSecurityManager()
182 if not sm
.checkPermission(change_images_and_files
, self
) :
183 raise Unauthorized("Tiling arbitrary zoom unauthorized")
184 if self
.makeTilesAt(zoom
) :
185 tile
= self
._tiles
[zoom
][x
][y
]
186 return tile
.index_html(REQUEST
=REQUEST
, RESPONSE
=RESPONSE
)
188 tile
= self
._tiles
[zoom
][x
][y
]
189 return tile
.index_html(REQUEST
=REQUEST
, RESPONSE
=RESPONSE
)