src/reportlab/lib/utils.py
branchrtl-support
changeset 3467 4f25b3a34c5f
parent 3253 06194e4167b9
--- a/src/reportlab/lib/utils.py	Mon Nov 23 13:27:57 2009 +0000
+++ b/src/reportlab/lib/utils.py	Thu Oct 21 10:34:13 2010 +0000
@@ -46,9 +46,9 @@
     from UserDict import UserDict as _UserDict
 
 class CIDict(_UserDict):
-    def __init__(self,*a,**kw):
-        map(self.update, a)
-        self.update(kw)
+    def __init__(self,*args,**kwds):
+        for a in args: self.update(a)
+        self.update(kwds)
 
     def update(self,D):
         for k,v in D.items(): self[k] = v
@@ -80,7 +80,7 @@
         except KeyError:
             return dv
 
-    def has_key(self,k):
+    def __contains__(self,k):
         try:
             self[k]
             return True
@@ -293,7 +293,7 @@
         sys.path = opath
         msg = "recursiveimport(%s,baseDir=%s) failed" % (modulename,baseDir)
         if baseDir:
-            msg = msg + " under paths '%s'" % `path`
+            msg = msg + " under paths '%s'" % repr(path)
         raise ImportError, msg
 
 def recursiveGetAttr(obj, name):
@@ -341,7 +341,6 @@
         except ImportError:
             Image = None
     haveImages = Image is not None
-    if haveImages: del Image
 
 try:
     from cStringIO import StringIO as __StringIO
@@ -435,8 +434,8 @@
         if 'b' not in mode and os.linesep!='\n': s = s.replace(os.linesep,'\n')
         return getStringIO(s)
 
-import urllib
-def open_for_read(name,mode='b', urlopen=urllib.urlopen):
+import urllib2
+def open_for_read(name,mode='b', urlopen=urllib2.urlopen):
     '''attempt to open a file or URL for reading'''
     if hasattr(name,'read'): return name
     try:
@@ -446,7 +445,7 @@
             return getStringIO(urlopen(name).read())
         except:
             raise IOError('Cannot open resource "%s"' % name)
-del urllib
+del urllib2
 
 def open_and_read(name,mode='b'):
     return open_for_read(name,mode).read()
@@ -493,7 +492,7 @@
     return time_mktime((y,m,d,h,m,s,0,0,0))
 
 def rl_get_module(name,dir):
-    if sys.modules.has_key(name):
+    if name in sys.modules:
         om = sys.modules[name]
         del sys.modules[name]
     else:
@@ -519,18 +518,18 @@
 
 def _isPILImage(im):
     try:
-        from PIL.Image import Image
-        return isinstance(im,Image)
-    except ImportError:
+        return isinstance(im,Image.Image)
+    except AttributeError:
         return 0
 
 class ImageReader(object):
     "Wraps up either PIL or Java to get data from bitmaps"
     _cache={}
-    def __init__(self, fileName):
+    def __init__(self, fileName,ident=None):
         if isinstance(fileName,ImageReader):
             self.__dict__ = fileName.__dict__   #borgize
             return
+        self._ident = ident
         #start wih lots of null private fields, to be populated by
         #the relevant engine.
         self.fileName = fileName
@@ -579,27 +578,28 @@
                     try:
                         self._width,self._height,c=readJPEGInfo(self.fp)
                     except:
-                        raise RuntimeError('Imaging Library not available, unable to import bitmaps only jpegs')
+                        annotateException('\nImaging Library not available, unable to import bitmaps only jpegs\nfileName=%r identity=%s'%(fileName,self.identity()))
                     self.jpeg_fh = self._jpeg_fh
                     self._data = self.fp.read()
                     self._dataA=None
                     self.fp.seek(0)
             except:
-                et,ev,tb = sys.exc_info()
-                if hasattr(ev,'args'):
-                    a = str(ev.args[-1])+(' fileName=%r'%fileName)
-                    ev.args= ev.args[:-1]+(a,)
-                    raise et,ev,tb
-                else:
-                    raise
+                annotateException('\nfileName=%r identity=%s'%(fileName,self.identity()))
+
+    def identity(self):
+        '''try to return information that will identify the instance'''
+        fn = self.fileName
+        if not isinstance(fn,basestring):
+            fn = getattr(getattr(self,'fp',None),'name',None)
+        ident = self._ident
+        return '[%s@%s%s%s]' % (self.__class__.__name__,hex(id(self)),ident and (' ident=%r' % ident) or '',fn and (' filename=%r' % fn) or '')
 
     def _read_image(self,fp):
         if sys.platform[0:4] == 'java':
             from javax.imageio import ImageIO
             return ImageIO.read(fp)
         else:
-            import PIL.Image
-            return PIL.Image.open(fp)
+            return Image.open(fp)
 
     def _jpeg_fh(self):
         fp = self.fp
@@ -620,38 +620,42 @@
 
     def getRGBData(self):
         "Return byte array of RGB data as string"
-        if self._data is None:
-            self._dataA = None
-            if sys.platform[0:4] == 'java':
-                import jarray
-                from java.awt.image import PixelGrabber
-                width, height = self.getSize()
-                buffer = jarray.zeros(width*height, 'i')
-                pg = PixelGrabber(self._image, 0,0,width,height,buffer,0,width)
-                pg.grabPixels()
-                # there must be a way to do this with a cast not a byte-level loop,
-                # I just haven't found it yet...
-                pixels = []
-                a = pixels.append
-                for i in range(len(buffer)):
-                    rgb = buffer[i]
-                    a(chr((rgb>>16)&0xff))
-                    a(chr((rgb>>8)&0xff))
-                    a(chr(rgb&0xff))
-                self._data = ''.join(pixels)
-                self.mode = 'RGB'
-            else:
-                im = self._image
-                mode = self.mode = im.mode
-                if mode=='RGBA':
-                    self._dataA = ImageReader(im.split()[3])
-                    im = im.convert('RGB')
+        try:
+            if self._data is None:
+                self._dataA = None
+                if sys.platform[0:4] == 'java':
+                    import jarray
+                    from java.awt.image import PixelGrabber
+                    width, height = self.getSize()
+                    buffer = jarray.zeros(width*height, 'i')
+                    pg = PixelGrabber(self._image, 0,0,width,height,buffer,0,width)
+                    pg.grabPixels()
+                    # there must be a way to do this with a cast not a byte-level loop,
+                    # I just haven't found it yet...
+                    pixels = []
+                    a = pixels.append
+                    for i in range(len(buffer)):
+                        rgb = buffer[i]
+                        a(chr((rgb>>16)&0xff))
+                        a(chr((rgb>>8)&0xff))
+                        a(chr(rgb&0xff))
+                    self._data = ''.join(pixels)
                     self.mode = 'RGB'
-                elif mode not in ('L','RGB','CMYK'):
-                    im = im.convert('RGB')
-                    self.mode = 'RGB'
-                self._data = im.tostring()
-        return self._data
+                else:
+                    im = self._image
+                    mode = self.mode = im.mode
+                    if mode=='RGBA':
+                        if Image.VERSION.startswith('1.1.7'): im.load()
+                        self._dataA = ImageReader(im.split()[3])
+                        im = im.convert('RGB')
+                        self.mode = 'RGB'
+                    elif mode not in ('L','RGB','CMYK'):
+                        im = im.convert('RGB')
+                        self.mode = 'RGB'
+                    self._data = im.tostring()
+            return self._data
+        except:
+            annotateException('\nidentity=%s'%self.identity())
 
     def getImageData(self):
         width, height = self.getSize()
@@ -661,7 +665,7 @@
         if sys.platform[0:4] == 'java':
             return None
         else:
-            if self._image.info.has_key("transparency"):
+            if "transparency" in self._image.info:
                 transparency = self._image.info["transparency"] * 3
                 palette = self._image.palette
                 try:
@@ -1110,7 +1114,9 @@
 
 def annotateException(msg,enc='utf8'):
     '''add msg to the args of an existing exception'''
+    if not msg: rise
     t,v,b=sys.exc_info()
+    if not hasattr(v,'args'): raise
     e = -1
     A = list(v.args)
     for i,a in enumerate(A):
@@ -1126,8 +1132,39 @@
                 msg=msg.encode(enc)
             else:
                 msg = str(msg)
-        A[e] += msg
+        if isinstance(v,IOError) and getattr(v,'strerror',None):
+            v.strerror = msg+'\n'+str(v.strerror)
+        else:
+            A[e] += msg
     else:
         A.append(msg)
     v.args = tuple(A)
     raise t,v,b
+    
+def escapeOnce(data):
+    """Ensure XML output is escaped just once, irrespective of input
+
+    >>> escapeOnce('A & B')
+    'A & B'
+    >>> escapeOnce('C & D')
+    'C & D'
+    >>> escapeOnce('E & F')
+    'E & F'
+
+    """
+    data = data.replace("&", "&")
+
+    #...but if it was already escaped, make sure it
+    # is not done twice....this will turn any tags
+    # back to how they were at the start.
+    data = data.replace("&", "&")
+    data = data.replace(">", ">")
+    data = data.replace("<", "<")
+    data = data.replace("&#", "&#")
+
+    #..and just in case someone had double-escaped it, do it again
+    data = data.replace("&", "&")
+    data = data.replace(">", ">")
+    data = data.replace("<", "<")
+    return data
+