thirdparty/google_appengine/google/appengine/api/images/__init__.py
changeset 828 f5fd65cc3bf3
parent 109 620f9b141567
child 1278 a7766286a7be
equal deleted inserted replaced
827:88c186556a80 828:f5fd65cc3bf3
    28     per execute_transforms() call.
    28     per execute_transforms() call.
    29 """
    29 """
    30 
    30 
    31 
    31 
    32 
    32 
       
    33 import struct
       
    34 
    33 from google.appengine.api import apiproxy_stub_map
    35 from google.appengine.api import apiproxy_stub_map
    34 from google.appengine.api.images import images_service_pb
    36 from google.appengine.api.images import images_service_pb
    35 from google.appengine.runtime import apiproxy_errors
    37 from google.appengine.runtime import apiproxy_errors
    36 
    38 
    37 
    39 
    81       raise NotImageError("Empty image data.")
    83       raise NotImageError("Empty image data.")
    82 
    84 
    83     self._image_data = image_data
    85     self._image_data = image_data
    84     self._transforms = []
    86     self._transforms = []
    85     self._transform_map = {}
    87     self._transform_map = {}
       
    88     self._width = None
       
    89     self._height = None
    86 
    90 
    87   def _check_transform_limits(self, transform):
    91   def _check_transform_limits(self, transform):
    88     """Ensure some simple limits on the number of transforms allowed.
    92     """Ensure some simple limits on the number of transforms allowed.
    89 
    93 
    90     Args:
    94     Args:
   101       transform_name = images_service_pb.ImagesServiceTransform.Type_Name(
   105       transform_name = images_service_pb.ImagesServiceTransform.Type_Name(
   102           transform)
   106           transform)
   103       raise BadRequestError("A '%s' transform has already been "
   107       raise BadRequestError("A '%s' transform has already been "
   104                             "requested on this image." % transform_name)
   108                             "requested on this image." % transform_name)
   105     self._transform_map[transform] = True
   109     self._transform_map[transform] = True
       
   110 
       
   111   def _update_dimensions(self):
       
   112     """Updates the width and height fields of the image.
       
   113 
       
   114     Raises:
       
   115       NotImageError if the image data is not an image.
       
   116       BadImageError if the image data is corrupt.
       
   117     """
       
   118     size = len(self._image_data)
       
   119     if size >= 6 and self._image_data.startswith("GIF"):
       
   120       self._update_gif_dimensions()
       
   121     elif size >= 8 and self._image_data.startswith("\x89PNG\x0D\x0A\x1A\x0A"):
       
   122       self._update_png_dimensions()
       
   123     elif size >= 2 and self._image_data.startswith("\xff\xD8"):
       
   124       self._update_jpeg_dimensions()
       
   125     elif (size >= 8 and (self._image_data.startswith("II\x2a\x00") or
       
   126                          self._image_data.startswith("MM\x00\x2a"))):
       
   127       self._update_tiff_dimensions()
       
   128     elif size >= 2 and self._image_data.startswith("BM"):
       
   129       self._update_bmp_dimensions()
       
   130     elif size >= 4 and self._image_data.startswith("\x00\x00\x01\x00"):
       
   131       self._update_ico_dimensions()
       
   132     else:
       
   133       raise NotImageError("Unrecognized image format")
       
   134 
       
   135   def _update_gif_dimensions(self):
       
   136     """Updates the width and height fields of the gif image.
       
   137 
       
   138     Raises:
       
   139       BadImageError if the image string is not a valid gif image.
       
   140     """
       
   141     size = len(self._image_data)
       
   142     if size >= 10:
       
   143       self._width, self._height = struct.unpack("<HH", self._image_data[6:10])
       
   144     else:
       
   145       raise BadImageError("Corrupt GIF format")
       
   146 
       
   147   def _update_png_dimensions(self):
       
   148     """Updates the width and height fields of the png image.
       
   149 
       
   150     Raises:
       
   151       BadImageError if the image string is not a valid png image.
       
   152     """
       
   153     size = len(self._image_data)
       
   154     if size >= 24 and self._image_data[12:16] == "IHDR":
       
   155       self._width, self._height = struct.unpack(">II", self._image_data[16:24])
       
   156     else:
       
   157       raise BadImageError("Corrupt PNG format")
       
   158 
       
   159   def _update_jpeg_dimensions(self):
       
   160     """Updates the width and height fields of the jpeg image.
       
   161 
       
   162     Raises:
       
   163       BadImageError if the image string is not a valid jpeg image.
       
   164     """
       
   165     size = len(self._image_data)
       
   166     offset = 2
       
   167     while offset < size:
       
   168       while offset < size and ord(self._image_data[offset]) != 0xFF:
       
   169         offset += 1
       
   170       while offset < size and ord(self._image_data[offset]) == 0xFF:
       
   171         offset += 1
       
   172       if (offset < size and ord(self._image_data[offset]) & 0xF0 == 0xC0 and
       
   173           ord(self._image_data[offset]) != 0xC4):
       
   174         offset += 4
       
   175         if offset + 4 < size:
       
   176           self._height, self._width = struct.unpack(
       
   177               ">HH",
       
   178               self._image_data[offset:offset + 4])
       
   179           break
       
   180         else:
       
   181           raise BadImageError("Corrupt JPEG format")
       
   182       elif offset + 2 <= size:
       
   183         offset += 1
       
   184         offset += struct.unpack(">H", self._image_data[offset:offset + 2])[0]
       
   185       else:
       
   186         raise BadImageError("Corrupt JPEG format")
       
   187     if self._height is None or self._width is None:
       
   188       raise BadImageError("Corrupt JPEG format")
       
   189 
       
   190   def _update_tiff_dimensions(self):
       
   191     """Updates the width and height fields of the tiff image.
       
   192 
       
   193     Raises:
       
   194       BadImageError if the image string is not a valid tiff image.
       
   195     """
       
   196     size = len(self._image_data)
       
   197     if self._image_data.startswith("II"):
       
   198       endianness = "<"
       
   199     else:
       
   200       endianness = ">"
       
   201     ifd_offset = struct.unpack(endianness + "I", self._image_data[4:8])[0]
       
   202     if ifd_offset < size + 14:
       
   203       ifd_size = struct.unpack(
       
   204           endianness + "H",
       
   205           self._image_data[ifd_offset:ifd_offset + 2])[0]
       
   206       ifd_offset += 2
       
   207       for unused_i in range(0, ifd_size):
       
   208         if ifd_offset + 12 <= size:
       
   209           tag = struct.unpack(
       
   210               endianness + "H",
       
   211               self._image_data[ifd_offset:ifd_offset + 2])[0]
       
   212           if tag == 0x100 or tag == 0x101:
       
   213             value_type = struct.unpack(
       
   214                 endianness + "H",
       
   215                 self._image_data[ifd_offset + 2:ifd_offset + 4])[0]
       
   216             if value_type == 3:
       
   217               format = endianness + "H"
       
   218               end_offset = ifd_offset + 10
       
   219             elif value_type == 4:
       
   220               format = endianness + "I"
       
   221               end_offset = ifd_offset + 12
       
   222             else:
       
   223               format = endianness + "B"
       
   224               end_offset = ifd_offset + 9
       
   225             if tag == 0x100:
       
   226               self._width = struct.unpack(
       
   227                   format,
       
   228                   self._image_data[ifd_offset + 8:end_offset])[0]
       
   229               if self._height is not None:
       
   230                 break
       
   231             else:
       
   232               self._height = struct.unpack(
       
   233                   format,
       
   234                   self._image_data[ifd_offset + 8:end_offset])[0]
       
   235               if self._width is not None:
       
   236                 break
       
   237           ifd_offset += 12
       
   238         else:
       
   239           raise BadImageError("Corrupt TIFF format")
       
   240     if self._width is None or self._height is None:
       
   241       raise BadImageError("Corrupt TIFF format")
       
   242 
       
   243   def _update_bmp_dimensions(self):
       
   244     """Updates the width and height fields of the bmp image.
       
   245 
       
   246     Raises:
       
   247       BadImageError if the image string is not a valid bmp image.
       
   248     """
       
   249     size = len(self._image_data)
       
   250     if size >= 18:
       
   251       header_length = struct.unpack("<I", self._image_data[14:18])[0]
       
   252       if ((header_length == 40 or header_length == 108 or
       
   253            header_length == 124 or header_length == 64) and size >= 26):
       
   254         self._width, self._height = struct.unpack("<II",
       
   255                                                   self._image_data[18:26])
       
   256       elif header_length == 12 and size >= 22:
       
   257         self._width, self._height = struct.unpack("<HH",
       
   258                                                   self._image_data[18:22])
       
   259       else:
       
   260         raise BadImageError("Corrupt BMP format")
       
   261     else:
       
   262       raise BadImageError("Corrupt BMP format")
       
   263 
       
   264   def _update_ico_dimensions(self):
       
   265     """Updates the width and height fields of the ico image.
       
   266 
       
   267     Raises:
       
   268       BadImageError if the image string is not a valid ico image.
       
   269     """
       
   270     size = len(self._image_data)
       
   271     if size >= 8:
       
   272       self._width, self._height = struct.unpack("<BB", self._image_data[6:8])
       
   273       if not self._width:
       
   274         self._width = 256
       
   275       if not self._height:
       
   276         self._height = 256
       
   277     else:
       
   278       raise BadImageError("Corrupt ICO format")
   106 
   279 
   107   def resize(self, width=0, height=0):
   280   def resize(self, width=0, height=0):
   108     """Resize the image maintaining the aspect ratio.
   281     """Resize the image maintaining the aspect ratio.
   109 
   282 
   110     If both width and height are specified, the more restricting of the two
   283     If both width and height are specified, the more restricting of the two
   330         raise Error()
   503         raise Error()
   331 
   504 
   332     self._image_data = response.image().content()
   505     self._image_data = response.image().content()
   333     self._transforms = []
   506     self._transforms = []
   334     self._transform_map.clear()
   507     self._transform_map.clear()
       
   508     self._width = None
       
   509     self._height = None
   335     return self._image_data
   510     return self._image_data
       
   511 
       
   512   @property
       
   513   def width(self):
       
   514     """Gets the width of the image."""
       
   515     if self._width is None:
       
   516       self._update_dimensions()
       
   517     return self._width
       
   518 
       
   519   @property
       
   520   def height(self):
       
   521     """Gets the height of the image."""
       
   522     if self._height is None:
       
   523       self._update_dimensions()
       
   524     return self._height
   336 
   525 
   337 
   526 
   338 def resize(image_data, width=0, height=0, output_encoding=PNG):
   527 def resize(image_data, width=0, height=0, output_encoding=PNG):
   339   """Resize a given image file maintaining the aspect ratio.
   528   """Resize a given image file maintaining the aspect ratio.
   340 
   529