1
#Copyright ReportLab Europe Ltd. 2000-2004
2
#see license.txt for license details
3
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/lib/utils.py
4
__version__=''' $Id$ '''
6
import string, os, sys, imp
8
from reportlab.lib.logger import warnOnce
9
SeqTypes = (ListType,TupleType)
10
if sys.hexversion<0x2030000:
14
def _findFiles(dirList,ext='.ttf'):
15
from os.path import isfile, isdir, join as path_join
16
from os import listdir
21
if not isdir(D): continue
24
if isfile(fn) and (not ext or fn.lower().endswith(ext)): A(fn)
30
from UserDict import UserDict as _UserDict
32
class CIDict(_UserDict):
33
def __init__(self,*a,**kw):
38
for k,v in D.items(): self[k] = v
40
def __setitem__(self,k,v):
45
_UserDict.__setitem__(self,k,v)
47
def __getitem__(self,k):
52
return _UserDict.__getitem__(self,k)
54
def __delitem__(self,k):
59
return _UserDict.__delitem__(self,k)
61
def get(self,k,dv=None):
79
return _UserDict.pop(*((self,k)+a))
81
def setdefault(self,k,*a):
86
return _UserDict.setdefault(*((self,k)+a))
89
#with the Mac, we need to tag the file in a special
90
#way so the system knows it is a PDF file.
91
#This supplied by Joe Strout
92
import macfs, macostools
94
'BMP' : ('ogle','BMP '),
95
'EPS' : ('ogle','EPSF'),
96
'EPSF': ('ogle','EPSF'),
97
'GIF' : ('ogle','GIFf'),
98
'JPG' : ('ogle','JPEG'),
99
'JPEG': ('ogle','JPEG'),
100
'PCT' : ('ttxt','PICT'),
101
'PICT': ('ttxt','PICT'),
102
'PNG' : ('ogle','PNGf'),
103
'PPM' : ('ogle','.PPM'),
104
'TIF' : ('ogle','TIFF'),
105
'TIFF': ('ogle','TIFF'),
106
'PDF' : ('CARO','PDF '),
107
'HTML': ('MSIE','TEXT'),
109
def markfilename(filename,creatorcode=None,filetype=None,ext='PDF'):
111
if creatorcode is None or filetype is None and ext is not None:
113
creatorcode, filetype = _KNOWN_MAC_EXT[string.upper(ext)]
116
macfs.FSSpec(filename).SetCreatorType(creatorcode,filetype)
117
macostools.touched(filename)
121
def markfilename(filename,creatorcode=None,filetype=None):
125
__RL_DIR=os.path.dirname(reportlab.__file__) #possibly relative
126
_RL_DIR=os.path.isabs(__RL_DIR) and __RL_DIR or os.path.abspath(__RL_DIR)
129
#Attempt to detect if this copy of reportlab is running in a
130
#file system (as opposed to mostly running in a zip or McMillan
131
#archive or Jar file). This is used by test cases, so that
132
#we can write test cases that don't get activated in a compiled
136
__file__ = sys.argv[0]
139
_isFSD = not __loader__
140
_archive = __loader__.archive
141
_archivepfx = _archive + os.sep
142
_archivedir = os.path.dirname(__loader__.archive)
143
_archivedirpfx = _archivedir + os.sep
144
_archivepfxlen = len(_archivepfx)
145
_archivedirpfxlen = len(_archivedirpfx)
146
def __startswith_rl(fn,
147
_archivepfx=os.path.normcase(_archivepfx),
148
_archivedirpfx=os.path.normcase(_archivedirpfx),
149
_archive=os.path.normcase(_archive),
150
_archivedir=os.path.normcase(_archivedir),
151
os_path_normpath=os.path.normpath,
152
os_path_normcase=os.path.normcase,
155
os_sep_len = len(os.sep)):
156
'''if the name starts with a known prefix strip it off'''
157
fn = os_path_normpath(fn.replace('/',os_sep))
158
nfn = os_path_normcase(fn)
159
if nfn in (_archivedir,_archive): return 1,''
160
if nfn.startswith(_archivepfx): return 1,fn[_archivepfxlen:]
161
if nfn.startswith(_archivedirpfx): return 1,fn[_archivedirpfxlen:]
162
cwd = os_path_normcase(os_getcwd())
164
if nfn.startswith(cwd):
165
if fn[n:].startswith(os_sep): return 1, fn[n+os_sep_len:]
166
if n==len(fn): return 1,''
167
return not os.path.isabs(fn),fn
169
def _startswith_rl(fn):
170
return __startswith_rl(fn)[1]
172
def rl_glob(pattern,glob=glob.glob,fnmatch=fnmatch.fnmatch, _RL_DIR=_RL_DIR,pjoin=os.path.join):
173
c, pfn = __startswith_rl(pattern)
176
r += map(lambda x,D=_archivepfx,pjoin=pjoin: pjoin(_archivepfx,x),filter(lambda x,pfn=pfn,fnmatch=fnmatch: fnmatch(x,pfn),__loader__._files.keys()))
179
_isFSD = os.path.isfile(__file__) #slight risk of wrong path
181
def _startswith_rl(fn):
183
def rl_glob(pattern,glob=glob.glob):
186
_isFSSD = _isFSD and os.path.isfile(os.path.splitext(__file__)[0] +'.py')
188
def isFileSystemDistro():
189
'''return truth if a file system distribution'''
192
def isCompactDistro():
193
'''return truth if not a file system distribution'''
196
def isSourceDistro():
197
'''return truth if a source file system distribution'''
202
### NOTE! FP_STR SHOULD PROBABLY ALWAYS DO A PYTHON STR() CONVERSION ON ARGS
203
### IN CASE THEY ARE "LAZY OBJECTS". ACCELLERATOR DOESN'T DO THIS (YET)
205
from _rl_accel import fp_str # in case of builtin version
207
from reportlab.lib._rl_accel import fp_str # specific
210
_log_10 = lambda x,log=log,_log_e_10=log(10.0): log(x)/_log_e_10
211
_fp_fmts = "%.0f", "%.1f", "%.2f", "%.3f", "%.4f", "%.5f", "%.6f"
213
_tz_re = re.compile('0+$')
216
if len(a)==1 and type(a[0]) in SeqTypes: a = a[0]
223
l = sa<=1 and 6 or min(max(0,(6-int(_log_10(sa)))),6)
228
if n[-1]=='.': n = n[:-1]
232
A((n[0]!='0' or len(n)==1) and n or n[1:])
233
return string.join(s)
235
#hack test for comma users
236
if ',' in fp_str(0.25):
239
return string.replace(apply(_FP_STR,a),',','.')
242
def get_rl_tempdir(*subdirs):
244
if _rl_tempdir is None:
246
_rl_tempdir = os.path.join(tempfile.gettempdir(),'ReportLab_tmp%s' % (sys.platform=='unix' and `os.getuid()` or ''))
248
if subdirs: d = os.path.join(*((d,)+subdirs))
255
def get_rl_tempfile(fn=None):
258
fn = tempfile.mktemp()
259
return os.path.join(get_rl_tempdir(),fn)
261
def recursiveImport(modulename, baseDir=None, noCWD=0, debug=0):
262
"""Dynamically imports possible packagized module, or raises ImportError"""
263
normalize = lambda x: os.path.normcase(os.path.abspath(os.path.normpath(x)))
264
path = map(normalize,sys.path)
266
if type(baseDir) not in SeqTypes:
269
tp = filter(None,list(baseDir))
272
if p not in path: path.insert(0,p)
275
for p in ('','.',normalize('.')):
277
if debug: print 'removed "%s" from path' % p
279
elif '.' not in path:
288
#make import errors a bit more informative
292
exec 'import %s\nm = %s\n' % (modulename,modulename) in locals()
297
msg = "recursiveimport(%s,baseDir=%s) failed" % (modulename,baseDir)
299
msg = msg + " under paths '%s'" % `path`
300
raise ImportError, msg
302
def recursiveGetAttr(obj, name):
303
"Can call down into e.g. object1.object2[4].attr"
304
return eval(name, obj.__dict__)
306
def recursiveSetAttr(obj, name, value):
307
"Can call down into e.g. object1.object2[4].attr = value"
308
#get the thing above last.
309
tokens = string.split(name, '.')
311
setattr(obj, name, value)
313
most = string.join(tokens[:-1], '.')
315
parent = recursiveGetAttr(obj, most)
316
setattr(parent, last, value)
323
from reportlab.rl_config import ZLIB_WARNINGS
324
if ZLIB_WARNINGS: warnOnce('zlib not available')
328
# Image Capability Detection. Set a flag haveImages
329
# to tell us if either PIL or Java imaging libraries present.
330
# define PIL_Image as either None, or an alias for the PIL.Image
331
# module, as there are 2 ways to import it
333
if sys.platform[0:4] == 'java':
336
import java.awt.image
342
from PIL import Image
348
haveImages = Image is not None
349
if haveImages: del Image
353
def getStringIO(buf=None):
354
'''unified StringIO instance interface'''
358
from cStringIO import StringIO
360
from StringIO import StringIO
361
__StringIO = StringIO
362
return buf is not None and __StringIO(buf) or __StringIO()
365
'''A type to allow clients of getArgvDict to specify a conversion function'''
366
def __init__(self,value,func):
370
def getArgvDict(**kw):
371
''' Builds a dictionary from its keyword arguments with overrides from sys.argv.
372
Attempts to be smart about conversions, but the value can be an instance
373
of ArgDictValue to allow specifying a conversion function.
375
def handleValue(v,av,func):
391
raise TypeError, "Can't convert string '%s' to %s" % (av,str(t))
396
for k, v in kw.items():
397
if isinstance(v,ArgvDictValue):
398
v, func = v.value, v.func
404
if string.find(a,ke)==0:
407
R[k] = handleValue(v,av,func)
411
if not handled: R[k] = handleValue(v,v,func)
415
def getHyphenater(hDict=None):
417
from reportlab.lib.pyHnj import Hyphen
418
if hDict is None: hDict=os.path.join(os.path.dirname(__file__),'hyphen.mashed')
420
except ImportError, errMsg:
421
if str(errMsg)!='No module named pyHnj': raise
424
def _className(self):
425
'''Return a shortened class name'''
427
name = self.__class__.__name__
428
i=string.rfind(name,'.')
429
if i>=0: return name[i+1:]
431
except AttributeError:
434
def open_for_read_by_name(name,mode='b'):
435
if 'r' not in mode: mode = 'r'+mode
437
return open(name,mode)
439
if _isFSD or __loader__ is None: raise
440
#we have a __loader__, perhaps the filename starts with
441
#the dirname(reportlab.__file__) or is relative
442
name = _startswith_rl(name)
443
s = __loader__.get_data(name)
444
if 'b' not in mode and os.linesep!='\n': s = s.replace(os.linesep,'\n')
445
return getStringIO(s)
448
def open_for_read(name,mode='b', urlopen=urllib.urlopen):
449
'''attempt to open a file or URL for reading'''
450
if hasattr(name,'read'): return name
452
return open_for_read_by_name(name,mode)
455
return getStringIO(urlopen(name).read())
457
raise IOError('Cannot open resource "%s"' % name)
460
def open_and_read(name,mode='b'):
461
return open_for_read(name,mode).read()
463
def open_and_readlines(name,mode='t'):
464
return open_and_read(name,mode).split('\n')
466
def rl_isfile(fn,os_path_isfile=os.path.isfile):
467
if hasattr(fn,'read'): return True
468
if os_path_isfile(fn): return True
469
if _isFSD or __loader__ is None: return False
470
fn = _startswith_rl(fn)
471
return fn in __loader__._files.keys()
473
def rl_isdir(pn,os_path_isdir=os.path.isdir,os_path_normpath=os.path.normpath):
474
if os_path_isdir(pn): return True
475
if _isFSD or __loader__ is None: return False
476
pn = _startswith_rl(os_path_normpath(pn))
477
if not pn.endswith(os.sep): pn += os.sep
478
return len(filter(lambda x,pn=pn: x.startswith(pn),__loader__._files.keys()))>0
480
def rl_get_module(name,dir):
481
if sys.modules.has_key(name):
482
om = sys.modules[name]
483
del sys.modules[name]
489
f, p, desc= imp.find_module(name,[dir])
490
return imp.load_module(name,f,p,desc)
492
if isCompactDistro():
493
#attempt a load from inside the zip archive
495
dir = _startswith_rl(dir)
496
dir = (dir=='.' or not dir) and _archive or os.path.join(_archive,dir.replace('/',os.sep))
497
zi = zipimport.zipimporter(dir)
498
return zi.load_module(name)
499
raise ImportError('%s[%s]' % (name,dir))
501
if om: sys.modules[name] = om
506
"Wraps up either PIL or Java to get data from bitmaps"
507
def __init__(self, fileName):
509
warnOnce('Imaging Library not available, unable to import bitmaps')
511
#start wih lots of null private fields, to be populated by
512
#the relevant engine.
513
self.fileName = fileName
517
self._transparent = None
519
self.fp = open_for_read(fileName,'b')
521
#detect which library we are using and open the image
522
if sys.platform[0:4] == 'java':
523
from javax.imageio import ImageIO
524
self._image = ImageIO.read(self.fp)
527
self._image = PIL.Image.open(self.fp)
530
if (self._width is None or self._height is None):
531
if sys.platform[0:4] == 'java':
532
self._width = self._image.getWidth()
533
self._height = self._image.getHeight()
535
self._width, self._height = self._image.size
536
return (self._width, self._height)
538
def getRGBData(self):
539
"Return byte array of RGB data as string"
540
if self._data is None:
541
if sys.platform[0:4] == 'java':
543
from java.awt.image import PixelGrabber
544
width, height = self.getSize()
545
buffer = jarray.zeros(width*height, 'i')
546
pg = PixelGrabber(self._image, 0,0,width,height,buffer,0,width)
548
# there must be a way to do this with a cast not a byte-level loop,
549
# I just haven't found it yet...
552
for i in range(len(buffer)):
554
a(chr((rgb>>16)&0xff))
555
a(chr((rgb>>8)&0xff))
557
self._data = ''.join(pixels)
559
rgb = self._image.convert('RGB')
560
self._data = rgb.tostring()
563
def getImageData(self):
564
width, height = self.getSize()
565
return width, height, self.getRGBData()
567
def getTransparent(self):
568
if sys.platform[0:4] == 'java':
571
if self._image.info.has_key("transparency"):
572
transparency = self._image.info["transparency"] * 3
573
palette = self._image.palette
575
palette = palette.palette
577
palette = palette.data
578
return map(ord, palette[transparency:transparency+3])
582
def getImageData(imageFileName):
583
"Get width, height and RGB pixels from image file. Wraps Java/PIL"
584
return ImageReader.getImageData(imageFileName)
587
'''Intended as a simple report back encapsulator
590
1) To record error data
591
dbg = DebugMemo(fn='dbgmemo.dbg',myVar=value)
592
dbg.add(anotherPayload='aaaa',andagain='bbb')
595
2) To show the recorded info
596
dbg = DebugMemo(fn='dbgmemo.dbg',mode='r')
600
3) To re-use recorded information
601
dbg = DebugMemo(fn='dbgmemo.dbg',mode='r')
603
myTestFunc(dbg.payload('myVar'),dbg.payload('andagain'))
605
in addition to the payload variables the dump records many useful bits
606
of information which are also printed in the show() method.
608
def __init__(self,fn='rl_dbgmemo.dbg',mode='w',getScript=1,modules=(),capture_traceback=1, stdout=None, **kw):
613
self.stdout = sys.stdout
615
if hasattr(stdout,'write'):
618
self.stdout = open(stdout,'w')
619
self.store = store = {}
620
if capture_traceback and sys.exc_info() != (None,None,None):
623
traceback.print_exc(None,s)
624
store['__traceback'] = s.getvalue()
626
lcwd = os.listdir(cwd)
627
exed = os.path.abspath(os.path.dirname(sys.argv[0]))
628
store.update({ 'gmt': time.asctime(time.gmtime(time.time())),
629
'platform': sys.platform,
630
'version': sys.version,
631
'hexversion': hex(sys.hexversion),
632
'executable': sys.executable,
633
'exec_prefix': sys.exec_prefix,
634
'prefix': sys.prefix,
638
'hostname': socket.gethostname(),
640
'byteorder': sys.byteorder,
641
'maxint': sys.maxint,
642
'maxint': getattr(sys,'maxunicode','????'),
643
'api_version': getattr(sys,'api_version','????'),
644
'version_info': getattr(sys,'version_info','????'),
645
'winver': getattr(sys,'winver','????'),
648
(sys,('getwindowsversion','getfilesystemencoding')),
649
(os,('uname', 'ctermid', 'getgid', 'getuid', 'getegid',
650
'geteuid', 'getlogin', 'getgroups', 'getpgrp', 'getpid', 'getppid',
656
store[a] = getattr(M,a)()
661
store.update({'exed': exed, 'lexed': os.listdir(exed),})
665
fn = os.path.abspath(sys.argv[0])
666
if os.path.isfile(fn):
668
store['__script'] = (fn,open(fn,'r').read())
672
for n,m in sys.modules.items():
673
if n=='reportlab' or n=='rlextra' or n[:10]=='reportlab.' or n[:8]=='rlextra.':
674
v = getattr(m,'__version__',None)
675
if v: module_versions[n] = v
676
store['__module_versions'] = module_versions
677
self.store['__payload'] = {}
681
payload = self.store['__payload']
682
for k, v in D.items():
690
pickle.dump(self.store,open(self.fn,'wb'))
694
self.store = pickle.load(open(self.fn,'rb'))
696
def _show_module_versions(self,k,v):
703
m = recursiveImport(k,sys.path[:],1)
704
d = getattr(m,'__version__',None)==vk and 'SAME' or 'DIFFERENT'
707
d = '??????unknown??????'
708
self._writeln(' %s = %s (%s)' % (k,vk,d))
710
def _banner(self,k,what):
711
self._writeln('###################%s %s##################' % (what,k[2:]))
714
self._banner(k,'Start ')
717
self._banner(k,'Finish ')
719
def _show_lines(self,k,v):
724
def _show_file(self,k,v):
725
k = '%s %s' % (k,os.path.basename(v[0]))
726
self._show_lines(k,v[1])
728
def _show_payload(self,k,v):
732
pprint.pprint(v,self.stdout)
735
specials = {'__module_versions': _show_module_versions,
736
'__payload': _show_payload,
737
'__traceback': _show_lines,
738
'__script': _show_file,
741
K = self.store.keys()
744
if k not in self.specials.keys(): self._writeln('%-15s = %s' % (k,self.store[k]))
746
if k in self.specials.keys(): apply(self.specials[k],(self,k,self.store[k]))
748
def payload(self,name):
749
return self.store['__payload'][name]
751
def __setitem__(self,name,value):
752
self.store['__payload'][name] = value
754
def __getitem__(self,name):
755
return self.store['__payload'][name]
757
def _writeln(self,msg):
758
self.stdout.write(msg+'\n')
762
if type(x) in SeqTypes: _flatten(x,a)
766
'''recursively flatten the list or tuple L'''
771
def find_locals(func,depth=0):
772
'''apply func to the locals at each stack frame till func returns a non false value'''
774
_ = func(sys._getframe(depth).f_locals)