/IronPython_Main/Hosts/MerlinWeb/examples/album.aspx.py

# · Python · 370 lines · 266 code · 61 blank · 43 comment · 73 complexity · 4083da9da5054b5c99d18df8e11a4fb2 MD5 · raw file

  1. # Copyright (c) Microsoft Corporation.
  2. #
  3. # This source code is subject to terms and conditions of the Apache License, Version 2.0. A
  4. # copy of the license can be found in the License.html file at the root of this distribution. If
  5. # you cannot locate the Apache License, Version 2.0, please send an email to
  6. # dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
  7. # by the terms of the Apache License, Version 2.0.
  8. #
  9. # You must not remove this notice, or any other, from this software.
  10. from System import DateTime, TimeSpan
  11. from System.Drawing import Color, Bitmap, Graphics, Point, Rectangle, RectangleF, Pens, Brushes, Font
  12. from System.Drawing.Drawing2D import InterpolationMode, LinearGradientBrush
  13. from System.Drawing.Imaging import ImageFormat
  14. from System.IO import Directory, File, Path, MemoryStream
  15. from System.Web import HttpContext, HttpRuntime, HttpUtility
  16. from System.Web.Caching import CacheDependency
  17. from System.Web.Hosting import HostingEnvironment
  18. # Customization
  19. ThumbnailSize = 120
  20. PreviewSize = 700
  21. ChildFolderColor = Color.LightGray
  22. ParentFolderColor = Color.LightSalmon
  23. # Helpers
  24. def BitmapToBytes(bitmap):
  25. ms = MemoryStream()
  26. bitmap.Save(ms, ImageFormat.Jpeg)
  27. ms.Close()
  28. return ms.ToArray()
  29. def GetCachedItem(itemType, path):
  30. cacheKey = 'py:albumitem:' + path
  31. # lookup in cache
  32. item = HttpRuntime.Cache.Get(cacheKey)
  33. if item is not None: return item
  34. # create new
  35. item = itemType(path)
  36. # cache it
  37. HttpRuntime.Cache.Insert(cacheKey, item, None, DateTime.MaxValue, TimeSpan.FromMinutes(4))
  38. return item
  39. # PICTURE respesents info about one picture
  40. class PICTURE:
  41. def __init__(self, filename):
  42. self.filename = filename
  43. self.name = Path.GetFileName(self.filename)
  44. self.__thumbnail = None
  45. self.__dims = None
  46. def __getDimensions(self):
  47. if self.__dims is None:
  48. origImage = Bitmap.FromFile(self.filename, False)
  49. self.__dims = (origImage.Width, origImage.Height)
  50. origImage.Dispose()
  51. return self.__dims
  52. def CalcResizedDims(self, size):
  53. width, height = self.__getDimensions()
  54. if width >= height and width > size:
  55. return size, int(float(size*height)/width)
  56. elif height >= width and height > size:
  57. return int(float(size*width)/height), size
  58. else:
  59. return width, height
  60. def DrawResizedImage(self, size):
  61. # load original image
  62. origImage = Bitmap.FromFile(self.filename, False)
  63. # calculate
  64. if self.__dims is None: self.__dims = (origImage.Width, origImage.Height)
  65. width, height = self.CalcResizedDims(size)
  66. # draw new image
  67. newImage = Bitmap(width, height)
  68. g = Graphics.FromImage(newImage)
  69. g.InterpolationMode = InterpolationMode.HighQualityBicubic
  70. g.DrawImage(origImage, 0, 0, width, height)
  71. imageBytes = BitmapToBytes(newImage)
  72. # cleanup
  73. g.Dispose()
  74. newImage.Dispose()
  75. origImage.Dispose()
  76. return imageBytes
  77. def __drawPictureThumbnail(self):
  78. # load original image
  79. origImage = Bitmap.FromFile(self.filename, False)
  80. # calculate
  81. size = ThumbnailSize
  82. if self.__dims is None: self.__dims = (origImage.Width, origImage.Height)
  83. width, height = self.CalcResizedDims(size)
  84. drawWidth, drawHeight = width, height
  85. width, height = max(width, size), max(height, size)
  86. drawXOffset, drawYOffset = (width-drawWidth)/2, (height-drawHeight)/2
  87. # draw new image
  88. newImage = Bitmap(width, height)
  89. g = Graphics.FromImage(newImage)
  90. g.InterpolationMode = InterpolationMode.HighQualityBicubic
  91. g.FillRectangle(Brushes.GhostWhite, 0, 0, width, height)
  92. g.DrawRectangle(Pens.LightGray, 0, 0, width-1, height-1)
  93. g.DrawImage(origImage, drawXOffset, drawYOffset, drawWidth, drawHeight)
  94. imageBytes = BitmapToBytes(newImage)
  95. # cleanup
  96. g.Dispose()
  97. newImage.Dispose()
  98. origImage.Dispose()
  99. return imageBytes
  100. def thumbnail(self):
  101. if self.__thumbnail is None: self.__thumbnail = self.__drawPictureThumbnail()
  102. return self.__thumbnail
  103. def GetPicture(filename):
  104. return GetCachedItem(PICTURE, filename)
  105. # FOLDER respesents info about one folder
  106. class FOLDER:
  107. def __init__(self, path):
  108. self.path = path
  109. self.name = Path.GetFileName(self.path)
  110. self.__thumbnail = None
  111. self.__parentThumbnail = None
  112. def __getFolderItems(self, picturesOnly, count):
  113. if picturesOnly:
  114. list = []
  115. else:
  116. def IsSpecialFolder(dir):
  117. d = Path.GetFileName(dir).lower()
  118. return d.startswith('_vti_') or d.startswith('app_') or d.startswith('bin') or d.startswith('aspnet_')
  119. list = [GetFolder(d) for d in Directory.GetDirectories(self.path)[:count] if not IsSpecialFolder(d)]
  120. count -= len(list)
  121. if count > 0:
  122. list += [GetPicture(p) for p in Directory.GetFiles(self.path, '*.jpg')[:count]]
  123. return list
  124. def GetFirstFolderItems(self, count): return self.__getFolderItems(False, count)
  125. def GetFolderItems(self): return self.__getFolderItems(False, 10000)
  126. def GetFolderPictures(self): return self.__getFolderItems(True, 10000)
  127. def __drawFolderThumbnail(self, parent):
  128. size = ThumbnailSize
  129. # create new image
  130. newImage = Bitmap(size, size)
  131. g = Graphics.FromImage(newImage)
  132. g.InterpolationMode = InterpolationMode.HighQualityBicubic
  133. # draw background
  134. if parent: bc = ParentFolderColor
  135. else: bc = ChildFolderColor
  136. b = LinearGradientBrush(Point(0, 0), Point(size, size), bc, Color.GhostWhite)
  137. g.FillRectangle(b, 0, 0, size, size)
  138. b.Dispose()
  139. g.DrawRectangle(Pens.LightGray, 0, 0, size-1, size-1)
  140. # draw up to 4 subitems
  141. folderItems = self.GetFirstFolderItems(4)
  142. delta = 10
  143. side = (size-3*delta)/2-1
  144. rects = (
  145. Rectangle(delta + 3 , delta + 12 , side, side),
  146. Rectangle(size / 2 + delta / 2 - 3 , delta + 12 , side, side),
  147. Rectangle(delta + 3 , size / 2 + delta / 2 + 6 , side, side),
  148. Rectangle(size / 2 + delta / 2 - 3 , size / 2 + delta / 2 + 6 , side, side))
  149. for rect, item in zip(rects, folderItems):
  150. subImage = Bitmap.FromStream(MemoryStream(item.thumbnail()), False)
  151. g.DrawImage(subImage, rect)
  152. subImage.Dispose()
  153. for rect in rects:
  154. g.DrawRectangle(Pens.LightGray, rect)
  155. # draw folder name
  156. if parent: name = '[..]'
  157. else: name = Path.GetFileName(self.path)
  158. f = Font('Arial', 10)
  159. g.DrawString(name, f, Brushes.Black, RectangleF(2, 2, size-2, size-2))
  160. f.Dispose()
  161. # get the bytes of the image
  162. imageBytes = BitmapToBytes(newImage)
  163. # cleanup
  164. g.Dispose()
  165. newImage.Dispose()
  166. return imageBytes
  167. def thumbnail(self):
  168. if self.__thumbnail is None: self.__thumbnail = self.__drawFolderThumbnail(False)
  169. return self.__thumbnail
  170. def parentThumbnail(self):
  171. if self.__parentThumbnail is None: self.__parentThumbnail = self.__drawFolderThumbnail(True)
  172. return self.__parentThumbnail
  173. def GetFolder(path):
  174. return GetCachedItem(FOLDER, path)
  175. # Virtual path helper
  176. class VPATH:
  177. def __init__(self, str):
  178. s = str.lower()
  179. if s != '/' and s.endswith('/'): s = s.rstrip('/')
  180. if not s.startswith('/') or s.find('/.') >= 0 or s.find('\\') >= 0:
  181. raise Exception('invalid virtual path %s' % (str))
  182. self.str = s
  183. def encode(self):
  184. return HttpUtility.UrlEncodeUnicode(self.str).Replace('\'', '%27').Replace('%2f', '/').Replace('+', '%20')
  185. def physicalPath(self): return HostingEnvironment.MapPath(self.str)
  186. def parent(self):
  187. s = self.str
  188. if s == '/': return None
  189. s = s[0:s.rfind('/')]
  190. if s == '': s = '/'
  191. return VPATH(s)
  192. def child(self, name):
  193. if self.str == '/': return VPATH('/'+name)
  194. else: return VPATH(self.str+'/'+name)
  195. def name(self):
  196. s = self.str
  197. if s == '/': return None
  198. return s[s.rfind('/')+1:]
  199. def isRoot(self): return self.str == VPATH.root().str
  200. def underRoot(self):
  201. r = VPATH.root().str
  202. s = self.str
  203. return r == '/' or r == s or s.startswith(r + '/')
  204. __rootVPath = None
  205. @staticmethod
  206. def root():
  207. if VPATH.__rootVPath is None: VPATH.__rootVPath = VPATH(Request.FilePath).parent()
  208. return VPATH.__rootVPath
  209. # Request modes
  210. PreviewImageMode = 1
  211. ThumbnailImageMode = 2
  212. ParentThumbnailImageMode = 3
  213. # IMAGETAG represents data needed to produce an HTML image
  214. class IMAGETAG:
  215. def __init__(self, mode, vpath):
  216. p = vpath.encode()
  217. self.Src = '?mode=%d&path=%s' % (mode, p)
  218. if mode in (ThumbnailImageMode, ParentThumbnailImageMode):
  219. if vpath.isRoot(): self.Link = '?'
  220. else: self.Link = '?path='+p
  221. self.Width, self.Height = ThumbnailSize, ThumbnailSize
  222. self.Alt = 'thumbnail for '+p
  223. elif mode == PreviewImageMode:
  224. self.Link = p
  225. self.Width, self.Height = GetPicture(vpath.physicalPath()).CalcResizedDims(PreviewSize)
  226. self.Alt = p
  227. # Request Processing
  228. # 'declare' events we could be handling
  229. def Page_Load(sender, e): pass
  230. def Page_Unload(sender, e): pass
  231. # parse the mode from the query string
  232. if Request.QueryString.mode is not None:
  233. mode = int(Request.QueryString.mode)
  234. else:
  235. mode = 0
  236. # parse the path from the query string
  237. if Request.QueryString.path is not None:
  238. vpath = VPATH(Request.QueryString.path)
  239. if not vpath.underRoot():
  240. raise HttpException('invalid path - not in the handler scope')
  241. else:
  242. vpath = VPATH.root()
  243. ppath = vpath.physicalPath()
  244. isFolder = Directory.Exists(ppath)
  245. if not isFolder and not File.Exists(ppath):
  246. raise HttpException('invalid path - not found')
  247. # perform the action depending on the mode
  248. if mode > 0:
  249. # response is an image
  250. if mode == PreviewImageMode:
  251. imageResponse = GetPicture(ppath).DrawResizedImage(PreviewSize)
  252. elif mode == ThumbnailImageMode:
  253. if isFolder: imageResponse = GetFolder(ppath).thumbnail()
  254. else: imageResponse = GetPicture(ppath).thumbnail()
  255. elif mode == ParentThumbnailImageMode:
  256. imageResponse = GetFolder(ppath).parentThumbnail()
  257. # output the image in Page_Unload event
  258. def Page_Unload(sender, e):
  259. response = HttpContext.Current.Response
  260. response.Clear()
  261. response.ContentType = 'image/jpeg'
  262. response.OutputStream.Write(imageResponse, 0, imageResponse.Length)
  263. else:
  264. pvpath = vpath.parent()
  265. # response is HTML
  266. if isFolder:
  267. # folder view
  268. def Page_Load(sender, e):
  269. global ParentLink
  270. Header.Title = vpath.str
  271. AlbumMultiView.ActiveViewIndex = 0
  272. if pvpath is not None and pvpath.underRoot():
  273. ParentLink = IMAGETAG(ParentThumbnailImageMode, pvpath)
  274. FolderViewParentLinkSpan.Visible = True
  275. ThumbnailList.DataSource = [IMAGETAG(ThumbnailImageMode, vpath.child(i.name))
  276. for i in GetFolder(ppath).GetFolderItems()]
  277. ThumbnailList.DataBind()
  278. else:
  279. def Page_Load(sender, e):
  280. global ParentLink, PreviousLink, PictureLink, NextLink
  281. # single picture details view
  282. AlbumMultiView.ActiveViewIndex = 1
  283. ParentLink = IMAGETAG(ParentThumbnailImageMode, pvpath)
  284. prevvpath = None
  285. nextvpath = None
  286. found = False
  287. for pict in GetFolder(pvpath.physicalPath()).GetFolderPictures():
  288. p = pvpath.child(pict.name)
  289. if p.str == vpath.str:
  290. found = True
  291. elif found:
  292. nextvpath = p
  293. break
  294. else:
  295. prevvpath = p
  296. if not found: prevvpath = None
  297. if prevvpath is not None:
  298. PreviousLink = IMAGETAG(ThumbnailImageMode, prevvpath)
  299. PreviousLinkSpan.Visible = True
  300. else:
  301. NoPreviousLinkSpan.Visible = True
  302. if found:
  303. PictureLink = IMAGETAG(PreviewImageMode, vpath)
  304. PictureLinkSpan.Visible = True
  305. if nextvpath is not None:
  306. NextLink = IMAGETAG(ThumbnailImageMode, nextvpath)
  307. NextLinkSpan.Visible = True
  308. else:
  309. NoNextLinkSpan.Visible = True